対応バージョン: rev.7 の全機能は JETCHECKER LINK v1.20.0 以降で利用できます。
対象: JETCHECKER LINK(計数結果の受信・保存ソフト)と自社システムを連携する開発者。 計数イベントの取り込み、両替レートの設定(店頭レートボードへの反映)、 店頭・バックヤードディスプレイへの表示(計数結果・見積もり・取引)、 伝票の印字(ESC/POS 透過)ができます。
この文書は外部提供用の仕様です。ここに書かれた内容は互換性を維持します (2章「互換性の約束」参照)。ここに書かれていない動作・ パラメータ・ファイル形式は内部実装であり、予告なく変わることがあります。
/v1/・イベントスキーマ v2・雛形規約 v1 などの
機能側のバージョンとは別物です)。本書の構成: API の仕様そのものは5章「エンドポイント」に 全13本をまとめて記載します。8章「レート設定ガイド」・ 9章「ディスプレイ表示ガイド」・10章「印刷ガイド」は 考え方と使い方の章で、必要に応じて5章を参照します。
127.0.0.1(localhost)のみで待ち受けます。
他の PC・LAN からは接続できません。読み取りに認証はありません
(同一 PC 内であることが境界です)。書き込みのみトークン認証が
必要です(5章「書き込み系 API の共通規則」参照)。本仕様(rev.7)では以下を約束します:
state の遷移、/v1/status printer 節の
各項目の意味。ジョブ失敗の reason の値は追加される
ことがあります(未知の値も「失敗」として扱ってください)。
10章「印刷ガイド」の組版ガイドは参考情報
(プリンター実機の挙動は機種依存)で、約束の対象外ですseq / event_id /
received_at / event または
raw+ingest_error)と in_window の意味data-jc 語彙)。
語彙の追加は互換変更として行います。規約 v2 が必要になった場合も、
移行期間中は v1 雛形の動作を維持します約束しないもの: 内蔵雛形(店頭表示・レートボード)のデザイン・版面は 対象外です(予告なく改善されます)。デザインを固定したい場合は 9.2「表示ページと雛形」の差し替え規則で自前の雛形を 置いてください(その規則自体は約束します)。
破壊的変更が必要になった場合は、パスを /v2/ として提供し、
/v1/ は移行期間中並行して維持します。
ポートは固定ではありません。必ず設定ファイルから実ポートを読んでください:
%LocalAppData%\JetChecker\connector.json を読む(JSON)http.last_port の値が、現在実際に待ち受けているポートhttp://127.0.0.1:<last_port>/v1/... へ接続する既定ポートは 17434 ですが、使用中の場合は自動で別ポート(+1 ずつ)に ずれるため、決め打ちしないでください。JETCHECKER LINK の再起動でポートが 変わることがあるので、接続エラー時は connector.json を読み直す実装を 推奨します。
event_id(ULID)と受信連番 seq(1 から単調増加)が
付与されます。イベントは追記専用で、一度保存されたものは変更・削除されません。pos-sync、
my-app)。事前の登録手続きはなく、決めた ID を client
パラメータで名乗って初めてアクセスした時点で、サーバー側にその ID の
取り込み位置(0 = 全件未取込)が作られます。webapp と、jc-(小文字)で始まる ID は
製品予約です。使用すると 400(code: "reserved_client")に
なります。また excel / csv / diag-vba は
付属機能が使用中のため、エラーにはなりませんが使わないでください
(付属機能とイベントを取り合います)。seq いくつまで取り込んだか」を JETCHECKER LINK が覚えており、
GET /v1/events はその位置より後のイベントだけを返し、
POST .../cursor で位置を進めます。クライアント側で位置を保存する
必要はありません。| # | メソッドとパス | 認証 | 内容 |
|---|---|---|---|
| 1 | GET /v1/events | 不要 | 未取込イベントの取得 |
| 2 | POST /v1/clients/{id}/cursor | 不要 | カーソル更新(取込確定) |
| 3 | GET /v1/status | 不要 | 状態取得(新着検知・接続状態) |
| 4 | GET /v1/rates | 不要 | レート一覧の取得 |
| 5 | POST /v1/rates | 必要 | レート変更/削除 |
| 6 | POST /v1/display/state | 必要 | 表示状態の設定/クリア(見積もり・取引・待機) |
| 7 | GET /v1/display/snapshot | 不要 | 表示スナップショット(表示ページのポーリング先) |
| 8 | GET /v1/events/{event_id} | 不要 | 単一イベントの取得 |
| 9 | GET /v1/display/rates | 不要 | レートボードデータの取得(ボードの描画データ) |
| 10 | GET /v1/display/rateboard | 不要 | レートボード設定の取得 |
| 11 | POST /v1/display/rateboard | 必要 | レートボード設定の変更(ヘッダー・列数・表示順) |
| 12 | POST /v1/print/raw | 必要 | 印刷(完成済み ESC/POS バイト列の透過印字) |
| 13 | GET /v1/print/jobs/{job_id} | 不要 | 印刷ジョブ状態(印字の完了確認) |
書き込み(エンドポイント5・6・11・12)にはトークン認証が必要です:
%LocalAppData%\JetChecker\connector.json の
http.print_token の値を読む
(名称は歴史的経緯によるもので、書き込み系 API 共通のトークンです)Authorization: Bearer <トークン> を付けるContent-Type: application/json を必ず付ける(無いと 415)トークン不一致・欠落は 401。ブラウザからの呼び出し(Origin ヘッダー付き)は
loopback オリジン以外 403 です(通常のネイティブアプリ・サーバープロセスからの
呼び出しには関係ありません)。お客様に見せる金額・レートを偽装できる口のため、
書き込みはすべてこの規則で保護されています。
また、レート・表示系の書き込み(エンドポイント5・6・11)のボディには
actor(実施者名)が必須です
(64文字以内・制御文字不可)。誰の操作かが JETCHECKER LINK 側の操作記録(監査ログ)と
レート履歴に残ります。JETCHECKER LINK の設定で実施者リストが登録されている場合は、
登録名との完全一致が要求されます(未登録名は 400)。
リスト未登録の店では自由入力です。
印刷(エンドポイント12)は actor の代わりに
client(発行元の識別子)を必須とし、印刷履歴に記録されます。
GET /v1/events?client=<id>[&limit=N]
client(必須): 利用者が決めたクライアントID。英数字と
. _ -、64文字以内。初めて使う ID は
取り込み位置 0(全件が未取込)から始まりますlimit(任意): 最大件数。省略時は全未取込応答は NDJSON(1行 = 1イベントの JSON、seq 昇順)。各行はエンベロープ形式です:
{"seq":131,"event_id":"01KXQQ...","received_at":"2026-07-17T18:50:46+09:00","event":{"schema_version":2,"currency":"JPY","total_value":93000,"...":"..."}}
| フィールド | 内容 |
|---|---|
seq | 受信連番(単調増加)。カーソル更新の基準値 |
event_id | ULID。重複排除・突合のキーは必ずこれを使う(12章「実装ガイド」) |
received_at | PC がイベントを受信した日時(ISO 8601)。信頼できる時刻はこちら |
event | ペイロード(計数イベント本体。6章「イベントスキーマ」)。無加工 |
raw / ingest_error | ペイロードが JSON として解釈できなかった場合のみ、event の代わりに原文と理由が入る。「異常でも保存し、捨てない」方針のため API でも返る |
in_window | キーが無い = 正規受信。false = 受信窓外の行(7章「受信窓」)。true は送られません(省略が正規) |
POST /v1/clients/{id}/cursor
Content-Type: application/json
{"cursor_seq": N}
「seq = N まで取り込み済み」を記録します。以後の取得は
seq > N のみ返ります。
seq を超える場合は 400(未受信イベントの黙殺防止){"client_id":"...","cursor_seq":N}GET /v1/status
{
"serial": {"port": "COM3", "connected": true},
"events": {"count": 141, "max_seq": 141, "last_received_at": "2026-07-18T16:00:02+09:00"},
"clients": [
{"client_id": "my-app", "type": "http", "cursor_seq": 138, "pending": 3}
]
}
events.max_seq のポーリングで行ってください
(前回値より増えていたら取得を実行)。1秒間隔程度までを目安としますserial.connected で計数機側ケーブルの接続状態を確認できますclients = 登録済みクライアントIDごとの取り込み位置の一覧。
pending = そのクライアントの未取込件数GET /v1/rates
有効レート(通貨ごとの最新値)を表示順で返します。
値の読み方(WE SELL / WE BUY・unit)は
8章「レート設定ガイド」参照。
{"rates": [{"currency": "USD", "we_sell": "159.24", "we_buy": "157.15",
"unit": 1, "base_fee": 200, "base_fee_pct": null, "inverted": false,
"changed_by": "山田", "source": "manual", "changed_at": "2026-08-01T12:00:00+09:00"}]}
inverted: true = WE SELL ≦ WE BUY の逆転(誤レートの疑い)base_fee = 両替手数料(円)。base_fee_pct が null なら
固定額制、値あり(文字列%)なら定率制で base_fee は最低手数料(円)
(8.1「手数料の適用のされ方」参照)POST /v1/rates
Authorization: Bearer <トークン>
Content-Type: application/json
{"actor": "山田", "currency": "USD", "we_sell": "159.24", "we_buy": "157.15", "unit": 1}
1通貨分の設定(新規・上書きとも同じ形)です。
| フィールド | 内容 |
|---|---|
actor(必須) | 実施者名(共通規則) |
currency(必須) | 通貨コード(英字3文字。小文字は大文字に正規化) |
we_sell / we_buy | レート(正の10進文字列。整数部9桁・小数部6桁まで)。null = その向きは未設定(掲示されない)。値の意味は8章 |
unit | 1 または 100(「100 KRW あたり」等)。省略時 1 |
base_fee | 手数料(円・0以上の整数)。省略時 0。固定額制ではこの額がそのまま手数料。定率制(base_fee_pct あり)では最低手数料になる(8.1) |
base_fee_pct | 定率手数料%(10進文字列。0 より大きく 100 未満)。省略・null = 固定額制 |
remove | true で削除({"actor","currency","remove":true})。履歴には残ります |
{"currency": "USD", "removed": false}。WE SELL ≦ WE BUY のときは
警告つきで受理されます(warning フィールド。
掲示前に確認してください){"error":"...","code":"bad_request"})POST /v1/display/state
Authorization: Bearer <トークン>
Content-Type: application/json
{
"client": "my-app",
"type": "exchange",
"refs": {"transaction_id": "X20260728-0002",
"events": [{"role": "in", "event_id": "01K..."}]},
"fields": {"title": "両替計算書", "amount_jpy": "¥45,000"},
"lines": ["両替計算書", "USD 300.00", "RATE 152.30", "¥45,000"]
}
店頭ディスプレイの表示内容を push します(表示の仕組み・使い方の流れは 9章「ディスプレイ表示ガイド」参照)。
| フィールド | 内容 |
|---|---|
client(必須) | 呼び出し元の識別子(イベント取得のクライアントID と同じ文字規則・予約規則。表示専用に別 ID でもよい) |
type(必須) | 表示の種別。JETCHECKER LINK は中身を解釈しません(表5-5 の推奨語彙参照)。予約語 idle = 表示クリア(このとき refs / fields / lines は無視) |
refs(任意) | 関連付け。transaction_id(自社の取引番号等)と events({role, event_id} の配列)。event_id は実在検証されます(未知・重複は 400)— イベントを取得してから push してください |
fields(任意) | 自由なキー→文字列値のマップ。中身は解釈されず、雛形が data-jc="tx.fields.<キー>" で拾います |
lines(任意) | 文字列の配列(表示本文)。既定雛形はこれを表示します |
title(fields.title)と lines を
必ず入れてください — 既定雛形が参照する標準キーはこの2つです
(独自雛形なら任意){"type": "exchange", "posted_at": "..."}。エラーは
code 付き(bad_request | unknown_event |
unauthorized | forbidden)| 値 | 意味 | 既定雛形の挙動 |
|---|---|---|
quote | 見積もり(確定前の提示。お客様との合意形成用) | 配色を変え「未確定」注記を表示。確定時刻を出さない(図9-2) |
exchange | 両替取引(確定) | 通常表示(図9-3) |
closing | レジ締め等の社内向け情報 | 店頭用雛形は表示しない(バックヤード用のみ) |
idle | (予約語)表示クリア | 待機画面へ(図9-4) |
上記以外の自由な値も送れます(既定雛形は通常表示。独自雛形での出し分けは 9.4「独自 type と雛形の紐づけ」)。
GET /v1/display/snapshot
表示ページのポーリング先です。独自の表示クライアント(サイネージ アプリ等)を作る場合はこれをポーリングしてください(1秒間隔で十分)。
{
"state": {"client": "my-app", "type": "exchange", "posted_at": "...",
"refs": {"...": "..."}, "fields": {"...": "..."}, "lines": ["..."]},
"latest_event": {"seq": 164, "event_id": "01K...", "received_at": "...",
"event": {"schema_version": 2, "...": "..."}}
}
state = 最後に push された表示状態。未 push・再起動後は null。
クリア後は {"type": "idle", "posted_at": "..."}latest_event = 最新イベントのエンベロープ(/v1/events と同形式)。
1件も無ければ nullGET /v1/events/{event_id}
エンベロープ形式で1件返します(未知の event_id は 404)。
特定の計数結果の固定表示(/display?event_id=<ULID>)や突合の再確認に
使います。
GET /v1/display/rates
店頭レートボード(両替レートを1通貨=1カードで掲示するモニター表示。図8-2)の 描画データです(店頭掲示と同じ公開情報のみで、手数料・変更者・履歴は 含まれません)。
ボードのページ /display/rates.html は本エンドポイントを10秒ごとに
取得して描画しています。このためエンドポイント5 で
レートを流し込めば、店頭掲示は常に自動で最新になります(掲示の貼り替え・
更新漏れがなくなる — ボードを使うこと自体が利点です)。
版面を独自デザインにしたい場合も、この自動更新の仕組みごと引き継げます:
表示雛形と同じファイル差し替え(9.2)で
%LocalAppData%\JetChecker\display\rates.html を自作ページ
(本エンドポイントをポーリングして描画。10秒間隔で十分)に置き換えれば、
キオスク用ショートカットにも LAN 表示(タブレット等の別デバイス。
セットアップ手順書参照)にも自作の版面がそのまま配信されます。
自社システムから本エンドポイントを読むだけの使い方もできます(掲示内容の確認・ 転記等)。ただし別オリジンに置いたブラウザページからの fetch は失敗します (読み取り API は CORS 許可ヘッダーを返しません。許可すると、店の PC のブラウザで開いた任意の Web サイトがローカルの計数データ・レートを読めてしまうためです。ブラウザを介さない呼び出し=自社サーバー・ネイティブアプリには関係ありません)。 掲示ページとして作るものは上記のファイル差し替えで JETCHECKER LINK に 配信させてください。
{
"as_of": "2026-08-11T12:00:00+09:00",
"rates": [
{"currency": "USD", "we_sell": "159.24", "we_buy": "157.15", "unit": 1,
"name_ja": "アメリカドル", "flag": "flag-usd.svg", "symbol": "$",
"changed_at": "2026-08-11T09:30:00+09:00"}
],
"header_lines": ["JetChecker 両替店"],
"max_columns": 4
}
rates は設定された表示順(エンドポイント11)。
we_sell / we_buy は10進文字列、未設定の側は
null(ボードは「—」表示)name_ja / flag / symbol は同梱の通貨情報
(未収載の通貨では空)。国旗画像は /display/<flag の値> で取得できますheader_lines(ヘッダー文言)・max_columns(1行の最大カード枚数)は
設定されている場合のみ現れます(設定はエンドポイント11)。
max_columns はボードの列数調整用の値(エンドポイント11)で、
差し替えた独自版面では無視して構いませんGET /v1/display/rateboard
店頭レートボードの表示設定です(店頭掲示と同じ公開情報)。 未設定の項目は空配列・0 で返ります。
{
"header_lines": ["JetChecker 両替店", "手数料込みのレートです"],
"max_columns": 4,
"currency_order": ["EUR", "USD", "KRW"]
}
POST /v1/display/rateboard
Authorization: Bearer <トークン>
Content-Type: application/json
{"actor": "山田", "header_lines": ["JetChecker 両替店"], "max_columns": 4,
"currency_order": ["EUR", "USD", "KRW"]}
部分更新: 送ったフィールドだけが変わり、省略したフィールドは 変わりません。JETCHECKER LINK の業務画面(「設定」→「店頭レートボード」)でも 同じ設定を編集でき、どちらで変えても同じ検証・同じ監査記録です。
| フィールド | 内容 |
|---|---|
actor(必須) | 実施者名(共通規則) |
header_lines | ヘッダー文言の配列(最大3行・各40文字以内・制御文字不可)。1行目が大きく(店名の格で)表示され、2行目以降は小さめの添え書き(図8-2 の上部)。空配列 [] = ヘッダーなし(レートカードだけの表示)に戻す |
max_columns | ボードの列数調整用の値。ボードの版面は窓の大きさと通貨数から1行のカード枚数を自動で決めるが、この値を設定するとそれが上限になる(縦長画面で文字が窮屈なときの逃げ道。0〜8、0 = 完全自動=既定)。差し替えた独自版面で使うかは自由 |
currency_order | 通貨コード(英字3文字)の配列。重複不可・40通貨まで。エンドポイント4「レート一覧」の応答順・レートボード・業務画面のレート表の並びに適用され、未収載の通貨は末尾に続く。空配列 [] = 既定の並びに戻す |
changed(変更された項目数)POST /v1/print/raw
Authorization: Bearer <トークン>
Content-Type: application/json
{
"client": "my-app",
"data": "G0BIZWxsbyEKCg==",
"refs": {},
"meta": {}
}
完成済みの ESC/POS バイト列を、接続ユニット経由でプリンターへそのまま送って 印字します。前提・制約・伝票の作り方は10章「印刷ガイド」を 先にお読みください(プリンターはシリアル接続モデルのみ対応です)。
| フィールド | 必須 | 内容 |
|---|---|---|
client | ○ | 発行元の識別子(英数字・. _ -、64文字以内)。印刷履歴(ジャーナル)に記録されます |
data | ○ | ESC/POS バイト列の base64(RFC 4648・パディングあり)。デコード後 1〜262,144 バイト(256KB) |
slip_type | — | 伝票種別の自由文字列。JETCHECKER LINK は解釈せず記録のみ |
refs | — | 引用申告(下記) |
meta | — | 自由形式 JSON。解釈せず記録のみ(シリアライズ後 16KB 以内) |
動作ルール:
data の中身は検査しません(キャッシュドロワーの開閉、プリンターの NV メモリ書き込み等の制御コマンドもそのまま通ります — 何を送るかはクライアントアプリの責任です)。カットや紙送りも一切自動で足しません。カットして排出したい場合は、カット命令まで含めて data を組んでください(10章)。[<機器ID> job=<ジョブ番号>] を無条件で印字します(無効化できません)。raw のジョブでは、クライアントが送ったバイト列の前(先頭)に必ずこの1行が付きます。これは「この紙は PC 発行である」ことを紙の上で判別するための仕組みで、ジョブ番号はエンドポイント13・印刷履歴と突き合わせるキーになります。なお JETCHECKER LINK 同梱の伝票(計数結果伝票・両替計算書)では同じスタンプが伝票の末尾に付きます(接続ユニットのソフトウェア 0.11.0 以降)。raw が先頭固定なのは意図的な違いです: raw はバイト透過で、クライアントが送るデータ内のカット命令の位置を JETCHECKER LINK が知り得ないため、末尾に付けるとカットの後=次の紙の先頭にスタンプが載ってしまいます。{"job_id": 123, "state": "queued"}。実際の印字はこの後に進みます(完了確認はエンドポイント13)。引用申告(refs)— 任意。「この紙はどの計数イベント/どの取引を根拠に刷ったか」を申告できます。申告すると印刷履歴に残り、後日「どのアプリがいつどのイベントを引用して刷ったか」を店舗側で確認(監査)できます:
"refs": {
"transaction_id": "X20260831-0001",
"events": [ {"role": "count", "event_id": "01KXQQ..."} ]
}
transaction_id: 自由文字列(64文字以内)。自社システムの取引 ID をそのまま使えます(JETCHECKER LINK は形式に関知しません)events[].role: 自由文字列(推奨: in / out / count)events[].event_id: 実在チェックがあります(未知の ID・同一リクエスト内の重複は 400)。エンドポイント1で得た event_id を渡してくださいジョブの動作ルール:
queue_full)。printer_unavailable)を即返します。接続復旧待ちの取り置きはしません(何分も後に突然紙が出る事故を防ぐため)。印刷ボタンの活性判定は /v1/status の serial.connected で行ってください。主なエラー(共通のエラー応答形式は11章):
400 bad_request(base64 不正・client の文字種違反・refs の不正)/
400 too_large(デコード後 256KB 超過・meta 16KB 超過)/
400 unknown_event(refs.events[].event_id が実在しない)/
503 printer_unavailable / 503 queue_full。
GET /v1/print/jobs/{job_id}
{
"job_id": 123, "kind": "raw", "client": "my-app", "state": "sending",
"total_bytes": 1832, "sent_bytes": 1024,
"created_at": "2026-08-31T14:00:12+09:00", "started_at": "2026-08-31T14:00:12+09:00",
"finished_at": null, "reason": null
}
state は queued(受理済み・送信待ち)→ sending(送信中)→ done または failed と遷移します。sent_bytes は接続ユニットが物理的にプリンターへ送出済みのバイト数です。進捗表示は実際の印字の進行とほぼ一致します。done は「接続ユニットがプリンターへ全バイトを書き切り、データ化けなしを確認した」という意味であり、紙が正しく出た保証ではありません(紙切れ・紙詰まりは検知しません。10章の注意事項も参照)。job_id は 404。reason(failed のときのみ。値は追加されることがあります=未知の値も失敗として扱う):
| 値 | 意味 |
|---|---|
unauth / busy / bad_line / bad_seq / crc_mismatch / timeout | 接続ユニットが報告した失敗(接続直後・別ジョブ実行中・転送データの破損・応答なし 等)。新規 POST でジョブごと再送してください |
too_large | ジョブが上限(256KB)超過 |
serial_lost | 送信中に計数機(接続ユニット)との接続が切れた |
baud_unsupported / foot_unsupported | 接続ユニットのソフトウェアが古く、店舗側のプリンター設定に対応していない(店舗側で接続ユニットの更新が必要) |
internal | JETCHECKER LINK 内部エラー |
event フィールドの中身(ペイロード)です。schema_version が 2 であることを
確認してから処理してください(未知のバージョンも配信はされます。その場合は保留するか
raw 相当として扱ってください)。
{
"schema_version": 2,
"device_id": "JC-A4C2F0",
"timestamp": "2026-03-26T14:26:38+09:00",
"currency": "JPY",
"mode": "value",
"denominations": [
{"denom": 2000, "qty": 2, "value": 4000},
{"denom": 1000, "qty": 5, "value": 5000}
],
"total_count": 7,
"total_value": 9000,
"verified": true
}
| フィールド | 内容 |
|---|---|
schema_version | スキーマ版。現行 2 |
device_id | 送信ユニットの個体ID(JC- + 英数字) |
timestamp | 計数機本体の時計による計数日時(ISO 8601)。機体の時計状態によっては過去日付(2000-01-01 起点など)になることがあります。時刻の判断にはエンベロープの received_at を使ってください |
currency | 通貨コード(英大文字3文字)。機体側で確定できない計数では空文字 "" |
mode | "value"(金額計数)/ "count"(枚数のみの計数) |
denominations | 金種内訳の配列 {denom(額面), qty(枚数), value(小計)}。count モードでは空配列 |
total_count | 合計枚数 |
total_value | 合計金額(count モードでは 0) |
verified | 金種内訳と合計の照合結果。false の行は計数値に不整合があった可能性があります(それでも保存・配信されます。error に理由) |
| フィールド | 内容 |
|---|---|
receipt_no | 計数機が採番する通し番号。機体の状態によりリセットされ得るため、一意キー・重複排除には使わないでください(event_id を使う) |
user_id | 計数機に設定されている操作者ID |
machine_sn | 計数機本体のシリアル番号(device_id とは別。複数台運用時の突合用) |
mix_index / mix_count | 混合通貨(CUR MIX)モードの計数結果でのみ現れます(通常計数ではキー自体なし)。1回の Mix 計数は通貨ごとに分割されて複数イベントになり、mix_count = 含まれていた通貨の数(=分割された行数)、mix_index = 通貨別の行に振られる連番(1〜mix_count。伝票上の出力順)。下記「Mix 計数の受信例」参照 |
serials | 紙幣1枚ごとのシリアル番号の配列 {denom, serial}(計数時の並び順を保持)。読み取れなかった紙幣は serial: null |
serials_verified | serials の枚数が total_count と一致するか(verified とは独立) |
error | 異常の内容(verified: false 時など) |
計数機の混合通貨(CUR MIX)モードで3通貨を一括計数すると、
GET /v1/events には3行のイベントとして届きます
(1イベント=1通貨の原則)。例:
{"seq":152,"event_id":"01KZG3W8A0EXAMPLE000000001","received_at":"2026-08-11T14:20:11+09:00","event":{"schema_version":2,"device_id":"JC-A4C2F0","receipt_no":"00123","machine_sn":"K7A012345","timestamp":"2026-08-11T14:20:05+09:00","currency":"JPY","mode":"value","denominations":[{"denom":10000,"qty":3,"value":30000},{"denom":1000,"qty":2,"value":2000}],"total_count":5,"total_value":32000,"verified":true,"mix_index":1,"mix_count":3}}
{"seq":153,"event_id":"01KZG3W8A1EXAMPLE000000002","received_at":"2026-08-11T14:20:11+09:00","event":{"schema_version":2,"device_id":"JC-A4C2F0","receipt_no":"00123","machine_sn":"K7A012345","timestamp":"2026-08-11T14:20:05+09:00","currency":"USD","mode":"value","denominations":[{"denom":100,"qty":2,"value":200},{"denom":20,"qty":1,"value":20}],"total_count":3,"total_value":220,"verified":true,"mix_index":2,"mix_count":3}}
{"seq":154,"event_id":"01KZG3W8A2EXAMPLE000000003","received_at":"2026-08-11T14:20:11+09:00","event":{"schema_version":2,"device_id":"JC-A4C2F0","receipt_no":"00123","machine_sn":"K7A012345","timestamp":"2026-08-11T14:20:05+09:00","currency":"CNY","mode":"value","denominations":[{"denom":100,"qty":6,"value":600}],"total_count":6,"total_value":600,"verified":true,"mix_index":3,"mix_count":3}}
読み方:
mix_count: 3 = この Mix 計数には3通貨が含まれていた。
mix_index 1〜3 が通貨別の行の連番(この例では JPY → USD → CNY の
伝票出力順)receipt_no / machine_sn / timestamp は
3行すべてに同じ値が複製されます — 「同じ1回の計数」として束ねる手がかりは
こちらです(mix_index はグループ内の何番目かを示すだけで、
グループの識別子ではありません)event_id と seq は行ごとに別
(全行が独立したイベントとして保存・配信されます)denominations / total_count / total_value /
serials はその行の通貨の分だけが入りますreceipt_no・timestamp の
mix_count 行」を1回の計数として扱ってください。Mix を使わない
運用ならこの2キーは無視して構いません(現れません)JETCHECKER LINK は接続ユニット(計数機側の送信機器)と定期的に相互認証を行い、
認証が成立している間だけ「受信窓」が開きます。窓が開いていない状態で届いた行
(認証に対応しない機器からの入力・PC 上の別ソフトによる注入など)にも保存・配信は
行われますが、エンベロープに in_window: false が付きます。
in_window: false の行は業務データとして採用しないことを
推奨します。JETCHECKER LINK 自身の業務機能(取引確定)も窓外の行の
取引化を拒否しますレート関連のエンドポイントは 4(一覧)・5(変更/削除)・ 9(ボードデータ)・10/11(ボード設定)です。 この章は値の意味と店頭への反映を説明します。
レート値の意味: 値は「その通貨 unit(1 または 100)単位あたりの円」で、
店側が主語です。we_sell = 店が外貨を売るレート
(お客様は外貨を買う)、we_buy = 店が外貨を買うレート。
正常な状態は WE SELL > WE BUY です。
currency_order と同じ設定)JETCHECKER LINK の業務画面が両替計算に使う規則です:
固定額制は base_fee そのまま。定率制は「お預り額の円換算 × %」を
円未満切り捨てで計算し、base_fee を下回る場合は
base_fee(=最低手数料)を適用します。
例: {"base_fee_pct": "2.0", "base_fee": 200} → 手数料はお預り額の2%、
ただし小額取引で計算額が 200 円に満たないときは 200 円。
設定したレートは、JETCHECKER LINK が配信する店頭レートボード
(http://127.0.0.1:<last_port>/display/rates.html)に自動反映されます
(約10秒以内)。レートボードは push 駆動の店頭表示(エンドポイント6)
とは独立した別ページで、表示状態の push や計数イベントの影響を
受けません(変わるのはレート変更と、エンドポイント11 の
ボード設定変更のときだけです)。ヘッダー文言(店名等)・カード枚数・通貨の表示順は
エンドポイント11 で設定できます。版面のデザイン自体は
本仕様の互換性約束の対象外です(データが反映されることのみ約束)。
別デバイス(HDMI スティック・タブレット等)での表示はセットアップ手順書の
「LAN 表示」を参照してください。版面を独自デザインにする場合は
エンドポイント9 の差し替え方式を参照してください。
/display/rates.html)。
エンドポイント5 のレート POST が約10秒以内にここへ反映される。上部のヘッダー文言・
表示順・列数はエンドポイント11 で設定(版面デザインは互換性約束の対象外)お客様向けの店頭ディスプレイ(2枚目モニター等)やバックヤードのモニターに、 計数結果・見積もり・両替取引を表示する機能です。関連エンドポイントは 6(表示状態の push)・7(スナップショット)・ 8(単一イベント)です。
構成(登場者は2者):
http://127.0.0.1:<last_port>/display、バックヤード用
/display/live.html)。約1秒間隔でエンドポイント7を
ポーリングして描画するだけの読み取り専用の観測者です。
何枚開いても構いません。開き方(キオスク用ショートカット等)は
セットアップ手順書の「ディスプレイ表示」を参照表示ページは常に次の3状態のいずれかです:
| 状態 | 表示内容 | きっかけ |
|---|---|---|
count | 最新の計数結果 | 計数イベントの受信(自動。API 発行不要) |
transaction | push された内容(見積もり・取引など) | POST /v1/display/state |
idle | 待機画面 | type: "idle" の push、または自動待機 |
切り替わりは後勝ち(last-write-wins)です:
idle へ落ちます
type: "quote")。
配色が変わり「未確定」注記が出る。確定時刻は表示されない
type: "exchange")。
push した lines がそのまま表示される
type: "idle" の push
または自動待機(既定180秒)でこの画面に戻る。図中央のロゴは
display\logo.png 設置時の表示例(未設置なら挨拶文のみ —
「自社ロゴの表示」参照)内蔵の表示ページ(雛形)は次の3枚です。内蔵雛形のデザインは互換性の約束の 対象外で、製品更新で予告なく改善されます:
/display — 店頭用(お客様向け。closing を表示しない)/display/live.html — バックヤード用(取引 push を無視して計数のみ表示)/display/rates.html — 店頭レートボードこれらのページは JETCHECKER LINK 本体に埋め込まれて配信されていますが、 配信時には先にデータ領域のフォルダーが確認されます。本書ではこの仕組みを ファイル差し替えと呼びます:
%LocalAppData%\JetChecker\display\ に同名ファイルを置くと、
内蔵版の代わりにそのファイルが配信されます(プログラム変更・
再インストール不要。反映はブラウザのリロードだけ)内蔵雛形は参考実装として、そのままコピーして編集の起点にできます。
URL パラメータ: ?event_id=<ULID> = そのイベントの固定表示
(自動更新なし)/ ?mode=counter|live = 雛形の用途切り替え。
雛形は HTML/CSS だけで書けます。ポーリング・状態判定・エスケープは
すべて同梱ランタイム /display/jetdisplay.js が担います(雛形から読み込むだけ)。
語彙は以下です(規約 v1。追加のみ行い、変更しません):
| 属性 | 場所 | 意味 |
|---|---|---|
data-jc="<パス>" | 任意要素 | スナップショット中の値を要素のテキストとして差し込み |
data-jc-format="number" | data-jc と併用 | 数値なら3桁区切りに整形 |
data-jc-format="datetime" | data-jc と併用 | ISO 8601 を 2026/07/30 14:26:38 表記に整形 |
data-jc-show="count transaction idle" | 任意要素 | 列挙した状態のときだけ表示(スペース区切り) |
data-jc-mode="counter|live" | body | live = 取引 push を無視(バックヤード用) |
data-jc-ignore-types="closing" | body | 列挙した type の push を無視(スペース区切り) |
data-jc-idle-seconds="180" | body | 最終更新から N 秒で待機表示(0=無効。既定180) |
パスの名前空間:
count.seq / count.event_id / count.received_at /
count.event.<ドットパス>(計数イベント。event 以下は
イベントスキーマ v2 をそのまま辿る。配列は数字インデックス)tx.type / tx.posted_at / tx.fields.<キー> /
tx.lines(lines は改行結合。雛形側は
white-space: pre-wrap で組むと桁揃えが崩れません)data-jc-* 属性は無視されます
(前方互換)ランタイムは <html data-jc-state="count|transaction|idle"> を常時更新する
ため、状態別の見た目は CSS だけで書けます
(例: [data-jc-state="idle"] .slip { display: none })。
宣言的語彙で足りない高度な雛形向けに、更新のたびに document へ
CustomEvent jc:update(detail = {state, snapshot})が
発火します。これを購読して自前 JS で描画しても構いません(その場合も表示ページから
書き込み API は叩けません — Origin 拒否)。
既定(店頭用)雛形は push された内容を次の規則で描画します(現行の参考実装の 挙動です。内蔵雛形のデザインは約束対象外=将来調整され得ます):
fields.title → 左パネルの見出し。「 / 」区切りで
日本語行+英語行の2行になる(区切りが無ければ1行)lines → 右パネルに上から並ぶ。空文字列の行は詰められる"お渡し金額 ¥46,945" —
間のスペースが2つ)。含まない行はそのまま1行表示図9-2(見積もり)を出す push:
{
"client": "my-app",
"type": "quote",
"fields": {"title": "両替 お見積もり"},
"lines": ["両替 お見積もり", "", "USD 300.00", "RATE 157.15",
"手数料 ¥200", "", "お渡し金額 ¥46,945"]
}
図9-3(両替取引の確定)を出す push(type を変え、
refs で計数イベントに紐付ける。refs は画面には出ません —
記録・突合用):
{
"client": "my-app",
"type": "exchange",
"refs": {"transaction_id": "X20260811-0001",
"events": [{"role": "in", "event_id": "01K..."}]},
"fields": {"title": "両替計算書"},
"lines": ["両替計算書", "", "USD 300.00", "RATE 157.15",
"手数料 ¥200", "", "お渡し金額 ¥46,945"]
}
図9-4(待機)に戻す push:
{"client": "my-app", "type": "idle"}
図9-1(計数結果)だけは push ではなくイベント受信で出ます — 計数機で数えた時点で自動表示され、API 発行は不要です。次のイベント(エンベロープ)を 受信したとき、既定雛形は図9-1 の画面を描画します:
{"seq":21,"event_id":"01KZ7VPDKM2YNCMFBDNDNCKW45","received_at":"2026-08-05T11:23:00+09:00",
"event":{"schema_version":2,"device_id":"JC-54155C","receipt_no":"SIM104","user_id":"User1",
"machine_sn":"SIM","timestamp":"2000-01-02T10:16:00+09:00","currency":"JPY","mode":"value",
"denominations":[{"denom":10000,"qty":2,"value":20000},{"denom":5000,"qty":1,"value":5000},
{"denom":1000,"qty":4,"value":4000}],
"total_count":7,"total_value":29000,"verified":true}}
画面との対応: currency → 「日本円 / JPY」(通貨名は雛形側の対応表)、
total_value → 合計金額、total_count → 合計枚数、
denominations → 金種別内訳、seq と received_at →
左下の「No.21 ・ 2026/08/05 11:23:00」。timestamp(機体時計。この例は
電池切れリセットで 2000年)は表示に使われません — エンベロープの
received_at が使われる実例です。画面確認には固定表示 URL
/display?event_id=<ULID> が使えます(エンドポイント8)。
既定雛形は /display/logo.png を参照します(待機画面の中央=図9-4 の位置と、
計数/取引画面の右下のマーク)。ロゴ画像は製品に同梱されません —
未設置の間、その場所には何も表示されません(待機画面は挨拶文のみ)。
自社ロゴを表示するには:
%LocalAppData%\JetChecker\display\logo.png に画像を置く
(雛形のファイル差し替えと同じ規則。消せば非表示に戻ります。
反映はブラウザのリロードのみ)雛形が type ごとに選ばれる仕組みはありません。開いている1枚の雛形に
すべての push が流れ込み、雛形側が type を見て出し分けます。
手段は3つ:
data-jc-ignore-types="closing maintenance" —
列挙した type の push をそのページでは無視する(宣言的)data-jc="tx.type" —
type 文字列そのものを差し込むjc:update を購読して
CSS クラスをトグルする(数行の JS)。同梱の店頭用雛形の quote 出し分け
(配色変更+「未確定」注記。図9-2)もこの方式です:<section id="tx" data-jc-show="transaction">...</section>
<script src="/display/jetdisplay.js"></script>
<script>
document.addEventListener("jc:update", function (e) {
var st = e.detail.snapshot.state; // 直近の push(未 push 時は null)
var isQuote = !!st && st.type === "quote";
document.getElementById("tx").classList.toggle("tx--quote", isQuote);
});
</script>
.tx--quote .heading { color: #a06000; } /* 見積もり時だけ配色を変える */
.tx--quote .note { display: block; } /* 「未確定」注記を出す */
補足: クラスの付け外しでできることは配色変更に限りません。既定雛形の quote は
「版面の構成は共通のまま、配色・注記・一部要素の表示だけ切り替える」という
軽い使い方の例ですが、type 専用の版面ブロック(<section> 等)を
複数用意してクラスで display を切り替えれば、type ごとに
全く別のレイアウトにもできます。jc:update の中は自由な JS なので、
type ごとに描画処理を分岐しても構いません。
独自 type(例: deposit、receipt-check)を送る場合も
同じパターンで、st.type の比較値を変えるだけです。判定に使える情報は
state の全フィールド(type / fields /
lines / refs)なので、type 以外(例: fields の
特定キーの有無)で出し分けることもできます。
「type ごとに全く別のレイアウト」の実例として、店頭表示に1ペイン全画面・ オレンジ系グラデーション背景・白抜き中央文字のお知らせ画面(お客様への案内の掲示。 図9-5)を追加する手順です。
修正するファイル:
%LocalAppData%\JetChecker\display\counter.html
(内蔵の店頭用雛形をコピーして配置=ファイル差し替え。内蔵版の入手は、
ブラウザで /display を開いて「ページのソースを保存」)。
追加は次の3点です。
① HTML — </main> の直後
(<script src="/display/jetdisplay.js"> の前)に
お知らせ用のセクションを追加:
<section class="notice">
<div class="notice-lines" data-jc="tx.lines"></div>
</section>
② CSS — </style> の直前に追加(全画面固定・中央寄せ・
グラデーション。notice 表示中は既存の2ペイン(main)と
ヘッダーを隠す):
.notice { display: none; position: fixed; inset: 0;
align-items: center; justify-content: center; text-align: center;
background: linear-gradient(135deg, #f7a021, #e2571b); color: #fff; }
.notice-lines { font-size: 6vmin; font-weight: 700; line-height: 2;
white-space: pre-wrap; }
body.is-notice .notice { display: flex; }
body.is-notice main, body.is-notice header { display: none; }
③ JS — 雛形末尾の <script> 内に追加(type が
notice のとき is-notice クラスを付ける):
document.addEventListener("jc:update", function (e) {
var st = e.detail.snapshot.state;
document.body.classList.toggle("is-notice", !!st && st.type === "notice");
});
push 例(図9-5 になります。data-jc="tx.lines" は
改行結合なので lines の1要素=画面の1行):
{
"client": "my-app",
"type": "notice",
"lines": ["MONEY EXCHANGE JETCHECKER", "受け取った現金はその場でご確認ください。"]
}
notice の全画面お知らせ。
上記の雛形3点追加+push 例の実行結果(実画面)この実装の持続性: 上記の JS は表示の状態機械
(count/transaction/idle)を見ずに「最後の push の type が
notice か」だけで判定しているため、計数イベントが入っても
自動待機時間が過ぎても消えません。消えるのは別の type
(idle = 通常運用へ、exchange 等)を push したときと、
JETCHECKER LINK の再起動時(表示状態は非永続=9.6)だけです。
「特定のディスプレイに常時掲示」の用途には push を使わない:
表示状態は全ディスプレイ共通の1スロットなので、quote/exchange を
push する営業運用と並行して notice を保持することはできません(exchange を
push した時点で上書きされます)。例えば「4枚目のモニターには営業案内を
出しっぱなしにしたい」場合は、push で運ぶのではなく内容を直接書いた
静的ページを %LocalAppData%\JetChecker\display\notice.html
等の別名で置き、そのディスプレイでは /display/notice.html を
開いてください(表示ページは別 URL を何枚でも開けます。内容の変更=
ファイル編集+リロード)。内容を API で更新し続けたい場合は、雛形側で
notice push を localStorage に記憶して出し続ける(以後の push に
影響されない)カスタム JS パターンも組めます。
表示系はどれも「事前に何かを送っておく → 表示ページが追従する」構造です。 push から表示反映までは約1秒(表示ページのポーリング間隔)です。
準備(全シナリオ共通): ディスプレイに表示ページを開いておきます (セットアップ手順書参照)。何も送っていない初期状態は待機画面(図9-4)です。
count。図9-1)を表示します自社システムがイベントを取り込んでいなくても表示されます(表示と取り込みは独立)。
/v1/status をポーリングし、events.max_seq の増加で計数を検知GET /v1/events?client=<自社ID> で計数イベントを取得
(event_id を控える)POST /v1/display/state — type: "quote"、
fields.title と lines に整形済みの見積もり内容 →
店頭に「未確定」注記付きで表示される(図9-2)POST /v1/display/state — type: "exchange"、
refs.events に手順2の event_id、lines に
確定内容 → 店頭が確定表示に変わる(図9-3)POST /v1/clients/<自社ID>/cursor で取込を確定type: "idle" を push して消すか、自動待機(既定180秒)に任せる見積もり(手順4)は任意の中間段階です。確定表示だけでよければ手順4〜5 を
飛ばして exchange を直接 push できますし、逆に提示のみで不成立に終わった
場合は idle を push して(または自動待機で)終わります。
POST /v1/display/state に
{"client": "...", "type": "idle"} を送ります。
次の計数または push まで待機画面になります。
エンドポイント5 でレートを POST するだけです(表示専用の API 発行は 不要)。ボードには約10秒以内に反映されます。ヘッダー・表示順は エンドポイント11 で設定できます(版面の独自化は エンドポイント9 の差し替え方式)。
quote を push し直してくださいdata-jc-idle-seconds を調整してください/v1/events を消費する必要はなく、逆に表示だけ使って
イベント取込をしない構成も可能ですOrigin 検査で拒否)。お客様の目に触れる端末で開いても、そこから
データを変更される経路はありませんエンドポイント12(印刷)・13(ジョブ状態)の 前提と、自前の伝票の作り方です。
POST /v1/print/raw に完成済みの ESC/POS バイト列を渡すと、JETCHECKER LINK が接続ユニット経由でプリンターへそのまま送って印字します。refs)。/v1/status の printer店舗に設置されたプリンターの情報は、店舗側が設置時のテスト印字で確定し、
GET /v1/status(認証不要)の printer 節で読めます:
"printer": { "kanji": true, "cutter": true, "cut": "part", "columns": 32, "baud": 9600,
"verified_at": "2026-08-31T10:12:00+09:00" }
| 項目 | 意味 | raw で刷るときの使い方 |
|---|---|---|
kanji | 漢字(Shift-JIS)フォント搭載か(null=未確定 / true / false) | false の機体に日本語を送らない(化け文字が連続印字されます)。false なら英語のみで組む |
cutter | オートカッター搭載か | true ならジョブ末尾にカット命令、false なら紙送り(LF×5 程度)で代替(10.3) |
cut | 店舗が選んだカット方式(part=ハーフカット / full=全カット。cutter: true のときのみ意味) | 店舗の好みに合わせるなら part→GS V 66 0 / full→GS V 65 0 を出し分ける(合わせなくても動作はします) |
columns | 1行の半角桁数(58mm→32 / 80mm→42 が目安) | 組版の桁数 W に使う(10.3)。未設定ならクライアント側の設定を使う |
baud | プリンターとの通信速度 | クライアントは意識不要(接続ユニットが吸収します) |
raw 経路は透過のため、これらの吸収(カット代替・言語切替)は自動では行われません。
printer 節を読んでクライアントアプリ側で出し分けてください。
上記以外の printer 項目は内部扱いです(未知の項目は無視してください)。
サーマルプリンターの紙幅は 58mm と 80mm が主流です。1行に入る文字数の目安:
| 紙幅 | 印字ドット幅 | 半角(英数字) | 全角(漢字・かな) |
|---|---|---|---|
| 58mm | 384 ドット | 32桁 | 16文字 |
| 80mm | 576 ドット | 42桁(機種により 48桁 等) | 21文字(同 24文字) |
/v1/status の printer.columns で読めます。最小構成のバイト列(同梱伝票と同じ構成。この順で data を組んでください):
(1) 先頭(日本語を使う場合の必須プロローグ)
FS & 1C 26 漢字モード ON
FS C 1 1C 43 01 漢字コード = Shift-JIS
ESC R 8 1B 52 08 国際文字セット = 日本(0x5C が ¥ で印字される)
(2) 本文
各行 = Shift-JIS エンコードしたテキスト + LF(0A)
(3) 末尾
LF LF 余白(2行)
GS V 66 0 1D 56 42 00 紙送り付きパーシャルカット
※ カッター無し機(printer.cutter=false)ではカットの代わりに LF×5
FS C 1 を送り忘れると全文が化けます(当社実測: ハイフンの連続が「⑬」の連打になる等)。プリンターの既定設定に依存せず、毎ジョブ自分で指定するのが raw の鉄則です。\1,000)。(1) の ESC R 8 により紙の上では ¥ になります。Unicode の ¥(U+00A5)は Shift-JIS に無いので使わないでください。ESC/POS コマンド早見(当社実機確認分。透過経路のため使えるコマンドはプリンター次第です):
| コマンド | バイト列 | 用途 | 注意 |
|---|---|---|---|
| 漢字モード ON | 1C 26 | プロローグ | |
| 漢字コード Shift-JIS | 1C 43 01 | プロローグ | 忘れると全文化け |
| 国際文字セット 日本 | 1B 52 08 | 0x5C → ¥ | |
| 白黒反転 ON/OFF | 1D 42 01 / 1D 42 00 | タイトル帯 | 非対応機あり。対応機でも漢字には効かない個体あり(ASCII・スペースのみ反転)。反転に意味を持たせない装飾に留める |
| 中央揃え ON/OFF | 1B 61 01 / 1B 61 00 | ロゴ等 | テキストはスペース詰めのセンタリングが確実 |
| 紙送り(n ドット) | 1B 4A nn | 余白 | |
| 紙送り付きパーシャルカット | 1D 56 42 00 | ジョブ末尾 | カッター無し機は無視した実測あり(ただし printer.cutter を読んで出し分けるのが確実) |
組版のコツ(同梱伝票の流儀):
| 要素 | ルール |
|---|---|
| 罫線 | - や = を W 個並べる |
| 店名・タイトル | スペース詰めでセンタリング |
| ラベル+金額 | ラベル左寄せ、金額を W 桁目に右詰め |
| 金種内訳 | 額面 x 枚数 小計 の3列。列位置は W から逆算して右詰め |
| 数値 | 3桁カンマ区切り。小数桁は通貨の最小単位(JPY=0桁、USD=2桁 等) |
| 日時 | YYYY/MM/DD HH:MM。PC 時刻を使う |
印字イメージ(32桁。1行目は接続ユニットが自動で付ける発行元スタンプです。 raw では先頭固定 — 同梱伝票では末尾に付くのと位置が異なります。 理由はエンドポイント12「発行元スタンプ」参照):
[JC-54155C job=1070]
○○両替ショップ
--------------------------------
受信 2026/08/31 14:26:40
店舗 S01 機器 JC-54155C
--------------------------------
━━━━ 計 数 結 果 ━━━━
--------------------------------
通貨 JPY
金種 枚数 小計
10,000 x 2 20,000
1,000 x 3 3,000
--------------------------------
合計 5 枚 \23,000
--------------------------------
import base64, json, time, urllib.request
# connector.json(%LocalAppData%\JetChecker\connector.json)から読む
PORT = 17434 # http.last_port
TOKEN = "xxxxxxxx" # http.print_token
ESC, FS, GS = b"\x1b", b"\x1c", b"\x1d"
W = 32 # 印字桁数(58mm。printer.columns 推奨)
def sj(s): # Shift-JIS 化(表示幅=バイト数)
return s.encode("shift_jis")
def line(s=""):
return sj(s) + b"\n"
def lr(label, value): # ラベル左寄せ+右詰め
pad = W - len(sj(label)) - len(sj(value))
return line(label + " " * max(pad, 1) + value)
body = FS + b"&" + FS + b"C\x01" + ESC + b"R\x08" # プロローグ(10.3)
body += line(" サンプル伝票")
body += line("-" * W)
body += lr("合計", "\\23,000") # \ は紙の上で ¥
body += line("-" * W)
body += b"\n\n" + GS + b"V\x42\x00" # 余白+カット
def call(path, payload=None):
req = urllib.request.Request(
f"http://127.0.0.1:{PORT}{path}",
data=None if payload is None else json.dumps(payload).encode(),
headers={"Content-Type": "application/json",
"Authorization": f"Bearer {TOKEN}"})
with urllib.request.urlopen(req) as r:
return json.load(r)
job = call("/v1/print/raw", {
"client": "my-app",
"slip_type": "sample",
"data": base64.b64encode(body).decode(),
})
while True: # 1秒ポーリングで十分
st = call(f"/v1/print/jobs/{job['job_id']}")
if st["state"] in ("done", "failed"):
break
time.sleep(1)
print(st["state"], st.get("reason"))
done は印刷完了の保証ではありません(エンドポイント13)。紙切れ・紙詰まり・プリンター側のボーレート不一致では、done でも紙が出ない/化けることがあります。導入時は必ず実機で試し刷りしてください。すべて {"error": "メッセージ"} の JSON です(書き込み系は機械可読の
code 付き)。400 = パラメータ・ボディ不正、401 = トークン不一致、
403 = オリジン拒否、404 = 対象なし(単一イベント取得・印刷ジョブ状態)、
405 = メソッド不正、415 = Content-Type 不正、500 = 内部エラー、
503 = 印刷不可(接続ユニット未接続 printer_unavailable・
待ち行列満杯 queue_full。エンドポイント12参照)。
推奨する取り込みループ:
/v1/status をポーリングし、events.max_seq が
前回より増えたら次へGET /v1/events?client=<自社ID> で未取込を取得event_id で冪等に
(同じ event_id を二度受け取っても二重登録しない)。
in_window: false の行は業務データとして採用しないseq で POST .../cursor を呼ぶ注意点:
verified: false や raw 付きの行も配信されます。
除外するかどうかは受信側の業務判断です(データとしては保存されています)http.last_port を読み直してくださいcurl http://127.0.0.1:17434/v1/status
curl "http://127.0.0.1:17434/v1/events?client=my-app"
curl -X POST -H "Content-Type: application/json" -d "{\"cursor_seq\":141}" http://127.0.0.1:17434/v1/clients/my-app/cursor
curl http://127.0.0.1:17434/v1/rates
curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer <トークン>" ^
-d "{\"actor\":\"担当者\",\"currency\":\"USD\",\"we_sell\":\"159.24\",\"we_buy\":\"157.15\"}" ^
http://127.0.0.1:17434/v1/rates
curl http://127.0.0.1:17434/v1/display/snapshot
curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer <トークン>" ^
-d "{\"client\":\"my-app\",\"type\":\"quote\",\"fields\":{\"title\":\"お見積もり\"},\"lines\":[\"USD 300.00\",\"RATE 152.30\",\"¥45,000\"]}" ^
http://127.0.0.1:17434/v1/display/state
curl http://127.0.0.1:17434/v1/display/rates
curl http://127.0.0.1:17434/v1/display/rateboard
curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer <トークン>" ^
-d "{\"actor\":\"担当者\",\"header_lines\":[\"JetChecker 両替店\"],\"max_columns\":4}" ^
http://127.0.0.1:17434/v1/display/rateboard
PowerShell:
$cfg = Get-Content "$env:LocalAppData\JetChecker\connector.json" | ConvertFrom-Json
$port = $cfg.http.last_port
Invoke-RestMethod "http://127.0.0.1:$port/v1/status"
Invoke-RestMethod "http://127.0.0.1:$port/v1/events?client=my-app"
Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '{"cursor_seq":141}' "http://127.0.0.1:$port/v1/clients/my-app/cursor"
Invoke-RestMethod "http://127.0.0.1:$port/v1/rates"
Invoke-RestMethod -Method Post -ContentType 'application/json' `
-Headers @{Authorization = "Bearer $($cfg.http.print_token)"} `
-Body '{"actor":"担当者","currency":"USD","we_sell":"159.24","we_buy":"157.15"}' `
"http://127.0.0.1:$port/v1/rates"
Invoke-RestMethod "http://127.0.0.1:$port/v1/display/snapshot"
Invoke-RestMethod -Method Post -ContentType 'application/json' `
-Headers @{Authorization = "Bearer $($cfg.http.print_token)"} `
-Body '{"client":"my-app","type":"quote","fields":{"title":"お見積もり"},"lines":["USD 300.00","RATE 152.30","¥45,000"]}' `
"http://127.0.0.1:$port/v1/display/state"
Invoke-RestMethod "http://127.0.0.1:$port/v1/display/rates"
Invoke-RestMethod "http://127.0.0.1:$port/v1/display/rateboard"
Invoke-RestMethod -Method Post -ContentType 'application/json' `
-Headers @{Authorization = "Bearer $($cfg.http.print_token)"} `
-Body '{"actor":"担当者","header_lines":["JetChecker 両替店"],"max_columns":4}' `
"http://127.0.0.1:$port/v1/display/rateboard"
以下は本製品の内部で使用しているもので、互換性の約束の対象外です:
GET /v1/events の format パラメータ(CSV 表現。
付属 Excel テンプレート専用)excel / csv / diag-vba
(付属機能が使用中のため使わないこと。webapp・jc-* は
400 で拒否されます — 4章参照)http.last_port・http.print_token 以外の項目/v1/print/raw・/v1/print/jobs/{job_id}
以外(テキスト変換経路 /v1/print/text・テスト印字等。
raw とジョブ状態はエンドポイント12・13)・
業務画面(/app)・
/v1/webapp/* 全体(レート API の公開パスは
/v1/rates です。/v1/webapp/rates は同梱業務画面用の
内部パスで、同じ動作をしますが互換性の約束は /v1/rates のみ)