CoinCore スタートガイド

はじめに

このガイドではCoinCoreの使い方について説明します.このガイドを読むと以下のことについて理解出来るようになります.

  • CoinCoreの概要
  • 独自の仮想通貨の作成方法
  • コインの振る舞いの定義方法
  • APIの使い方

前提

このガイドで示すサンプルコードを実行するためには以下のソフトウェアのインストールが必要となります.

CoinCore概要

CoinCoreとは独自の仮想通貨を作成し利用するための汎用的なミドルウェアです.CoinCoreを用いることにより例えば以下の事が実現出来ます.

  • 独自の仮想通貨や電子マネー(以下コインと呼ぶ)の作成
    • B2CやC2Cで用いられる仮想通貨や電子マネーを作成できます
  • コインに対する振る舞いの定義
    • チャージ,支払い,払戻し等の振る舞いを自由に定義できます
  • 口座の作成
    • コインの所有者(Owner),加盟店(Merchant),消費者(Consumer)を区別する口座を作成できます
  • 口座残高の管理
    • 口座ごとの現在残高の管理ができます
  • 取引履歴の閲覧
    • 特定の取引に関する取引履歴の閲覧や,特定口座に対する特定期間の取引履歴の閲覧等ができます

サンプルコインの例

以下に示すシナリオに従い独自コインの作成,コインの振る舞いの定義(チャージ,支払い),コインを使用したチャージと支払い操作の例を示します.

サンプルシナリオ

A銀行は地域の商店街向けにコインを提供します.コインは通常のコイン(Regularコイン)と,チャージの際に付与されるボーナス用コイン(Bonusコイン)の二種類のコインがあります.A銀行がコインに対する所有者口座を持ち,コインによる決済を受け付ける加盟店は加盟店口座を持ち,コインを用いて商品を購入する消費者は消費者口座を持ちます.消費者はあらかじめ自分の口座にコインをチャージしておき,商品を購入する際はコイン残高の範囲内で支払います.支払いにはRegularコインまたはBonusコインを使用します.
なお,消費者がチャージする際はボーナスとしてチャージ額の5%のコインが当該消費者の口座に付与され,消費者が加盟店で支払いをする際は,支払額の2%が手数料として当該加盟店の口座から徴収されます.

以下に,コインの種類,口座の種類,コインに対する振る舞いを整理します.

  • コインの種類
    • Regularコイン: 消費者が商品の購入に使用されるコイン.チャージの際に付与される
    • Bonusコイン: 消費者が商品の購入に使用されるコイン.Regularコインのチャージの際にボーナスとして付与される
    • Merchantコイン: 消費者が商品を購入の際に,加盟店が受け取るコイン
  • 口座の種類
    • Owner: コインの所有者(A銀行)の口座(CoinCoreが暗黙的に作成)
    • Merchant: 各加盟店用の口座(CoinCoreの利用者が明示的に作成)
    • Consumer: 商店街で商品を購入する消費者の口座(CoinCoreの利用者が明示的に作成)
  • コインに対する振る舞い
    • チャージ
      • A銀行がユーザに指定額のRegularコインを付与する
      • チャージしたRegularコインの5%分のBonusコインをチャージボーナス消費者に付与する
    • 支払い
      • 消費者はRegularコインもしくはBonusコインから必要な額のコインを加盟店に支払う
      • A銀行は手数料として支払額の2%を加盟店から徴収する

CoinCoreの準備

CoinCoreのインストール

本ガイドにおいては,CoinCoreの実行ファイルが事前に準備されているものとして,話を進めていきます.

CoinCoreを用いたコインの設定

現時点(2017年4月)においては,コインの作成,および,コインに対する振る舞いの定義はJSONファイルにより以下のように定義しますが,今後は,より簡単に定義ができるように,改良が加えられる予定です.

コインの作成

最初にコインを作成します.

coin.json

{"ID":"regular","Label":"regular coin"}
{"ID":"bonus","Label":"bonus coin"}
{"ID":"merchant","Label":"merchant coin"}

ここでは,前述のシナリオの通り,3つのコインを定義しています.コインのIDとして一意な文字列を指定することにより,コインを定義することができます.以降では,このIDにより,コインを区別します.Labelは,CoinCoreの利用者に対するコインの説明文です.

ここでは,各コインの名前を定義しただけなので,それぞれがどのように用いられるものであるか(コインに対する振る舞い)はまだ定義していません.

コインに対する振る舞いの定義とトランザクション

次に,コインに対する振る舞いを定義します.コインに対する振る舞いは,イベント,グループ,アクションの3段階で定義します.イベントにおいては,消費者からみたコインに対する振る舞いを定義し,グループにおいては,各イベントに対して,コインごとの振る舞いを定義し,アクションにおいては,各グループに対して,具体的にどのような振る舞い(コインの加算,減算処理)が必要かを定義します.

CoinCoreにおいては,イベントに対する振る舞いを一つのトランザクションとして扱います.トランザクションは,イベントごとに,1つの金額,複数の口座とともに指定されます.例えば,このシナリオにおけるチャージというイベントは,チャージの金額(数値),コインの所有者の口座から消費者の口座への入金なので,2つのそれぞれの口座とともに指定されます.

イベントの定義

はじめに,イベントを定義します.上述のシナリオに従い,チャージと支払いの2つのイベントを定義します.

event.json

{"ID":"charge","CoinID":"regular","Description":"sample definition of charge"}
{"ID":"payment","CoinID":null,"Description":"sample definition of payment"}

イベントの定義においては,まずイベントごとにIDを指定します.イベントにおけるCoinIDにおいては,(特にグループ,アクションで指定がない場合において)イベントがどのコインに対して優先的に振る舞うかを指定します.nullは,特定のコインに対して優先的に振る舞わないことを意味します.なお,どう振る舞うかについては,以降のグループおよびアクションで定義します.

グループの定義

次に,グループを定義します.

event_group.json

{"EventID":"charge","ID":"charge_0","CoinID":"bonus"}
{"EventID":"charge","ID":"charge_1","CoinID":null}
{"EventID":"payment","ID":"payment_0","CoinID":null}
{"EventID":"payment","ID":"payment_1","CoinID":"merchant"}

chargeイベントにおいては,Regularコインに対する振る舞いに加えて,チャージボーナス用のBonusコインに対する振る舞いが必要であるため,それぞれを定義しています.IDにおいては,それぞれの振る舞いを特定する一意な文字列を指定しています.2行目ではCoinIDにnullが指定されていますが,これはイベントの定義において,regularを優先的に使うように定義されているため,暗黙的にregularが指定されていると解釈してください.paymentイベントにおいては,消費者が所有しているコインから加盟店への支払いが起きるため,消費者のコインに対する振る舞いと,加盟店のコインに対する振る舞いを定義しています.消費者は2種類のコイン(RegularコインとBonusコイン)を所有することを想定しているので,ここではCoinID: nullを指定しています.

アクションの定義

最後に,アクションを定義します.

event_group_action.json

{"EventGroupID":"charge_0","ID":"charge_0_0","Type":1,"Target":0,"CoinID":null,"Description":"decrease owner balance for 5% bonus","Amount":null,"Percentage":5}
{"EventGroupID":"charge_0","ID":"charge_0_1","Type":0,"Target":1,"CoinID":null,"Description":"increase consumer balance","Amount":null,"Percentage":null}
{"EventGroupID":"charge_1","ID":"charge_1_0","Type":1,"Target":0,"CoinID":null,"Description":"decrease owner balance for regular","Amount":null,"Percentage":100}
{"EventGroupID":"charge_1","ID":"charge_1_1","Type":0,"Target":1,"CoinID":null,"Description":"increase consumer balance","Amount":null,"Percentage":null}
{"EventGroupID":"payment_0","ID":"payment_0_0","Type":1,"Target":1,"CoinID":null,"Description":"decrease consumer balance","Amount":null,"Percentage":100}
{"EventGroupID":"payment_0","ID":"payment_0_1","Type":0,"Target":0,"CoinID":null,"Description":"increase owner balance","Amount":null,"Percentage":null}
{"EventGroupID":"payment_1","ID":"payment_1_0","Type":1,"Target":0,"CoinID":null,"Description":"decrease owner balance","Amount":null,"Percentage":100}
{"EventGroupID":"payment_1","ID":"payment_1_1","Type":2,"Target":0,"CoinID":null,"Description":"fee for owner balance","Amount":null,"Percentage":5}
{"EventGroupID":"payment_1","ID":"payment_1_2","Type":0,"Target":2,"CoinID":null,"Description":"increase merchant balance","Amount":null,"Percentage":null]

上述のように,アクションでは各グループごとの具体的な処理(加算・減算処理)を定義しています.chargeイベントにおけるbonusコインに対する振る舞い(charge_0)に対して,
コインの所有者の口座の減算(chage_0_0)と消費者の口座の加算(charge_0_1)の2つのアクションが必要であると考えられるため,それぞれを定義しています.Typeは加算(1)であるか減算(0)であるかを表し,Targetは誰(コインの所有者:0,消費者:1,加盟店:2 等)に対してのアクションか,
を表しています.Percentageは,トランザクションの金額に対する割合を表しており,charge_0_0の場合はトランザクションの金額の5%をチャージボーナスとする,ということが定義されています.

同様に,chargeイベントにおけるregularコインに対する振る舞い(charge_1)に対して,コインの所有者の口座の減算(charge_1_0)と消費者の口座の加算(charge_1_1)の2つのアクションを定義しています.

paymentイベントに関しても同様に定義されています.まず,消費者のコインに対する振る舞い(payment_0)に対して,消費者の口座の減算(payment_0_0)とコイン所有者の口座の加算(payment_0_1)の2つのアクションが定義されています.次に,加盟店のコインに対する振る舞い(payment_1)に対して,コイン所有者の口座の減算(payment_1_0),コイン所有者の口座の加算(payment_1_1),加盟店の口座の加算(payment_1_2)の3つのアクションが定義されています.payment_1_1におけるType:2は,手数料の加算(加算の特殊系)を表します.なお,payment_0_1でコイン所有者の口座の加算を行い,payment_1_0でコイン所有者の口座の減算を行なっており(省略可能な)無駄な処理のように見えますが,CoinCoreの汎用性のためにこのように記述する必要があります.

アクションの定義においては,上述の例のように,現状の定義上では,コインごとに減算処理を先に記述し,次に加算処理を記述する必要があります.

CoinCoreを用いたトランザクションの実行

コインの設定ができましたので,次はCoinCoreを用いて実際のトランザクションの実行してみます.CoinCoreを用いたトランザクションの実行は,HTTPを用いたREST API形式で行います.

CoinCoreの起動

前準備として,CoreCoreをトランザクションを実行するデーモン(coincored)を起動します.

$ coincored -tls=false > coin.out 2>&1 &

口座の作成

最初に,CoreCoreのAPIを用いて口座を作成します.

POST /accounts

レスポンスでは口座IDがJSON形式で返されます.

以下にcurlコマンドによる実行例を示します.

$ curl -XPOST http://localhost:8080/accounts
{"id":"ebd26edf-9622-4eb3-44be-96efbf52ce6f"}
$ curl -XPOST http://localhost:8080/accounts
{"id":"c01d1c43-0e36-4680-48cf-53d003fd1732"}

ここでは,消費者口座のIDとしてebd26edf-9622-4eb3-44be-96efbf52ce6fを作成し,加盟店口座のIDとしてc01d1c43-0e36-4680-48cf-53d003fd1732を作成したものとします.

チャージ

次に,作成した消費者口座に対してチャージを行います.チャージにおいては,先に設定したチャージイベントのID,金額,口座を指定してトランザクションを実行します.

POST /transactions

Request Payload:
{event: "charge", amount: <チャージ額>, accounts: {consumer: <口座ID>}}

レスポンスではトランザクションIDがJSON形式で返されます.

以下にcurlコマンドによる実行例を示します.

$ curl -XPOST http://localhost:8080/transactions -d '{"event":"charge","amount":100,"accounts":{"consumer":"ebd26edf-9622-4eb3-44be-96efbf52ce6f"}}'
{"id":"eadfeb1c-73f4-4807-9c94-2d4fec0fe304"}

残高の確認

口座の残高は以下の方法で確認します.

GET /accounts/<口座ID>/balances

レスポンスでは口座残高がJSON形式で返されます.

curlコマンドによる実行例を以下に示します.

$ curl -s http://localhost:8080/accounts/ebd26edf-9622-4eb3-44be-96efbf52ce6f/balance
{"amount":105}

支払い

次に,消費者が加盟店で支払いを行います.支払いにおいては,先に設定した支払いイベントのトランザクションを実行します.

POST /transactions

Request Payload:
{event: "payment", amount: <支払い額>, accounts: {consumer: <消費者口座ID>, merchant: <加盟店口座ID>}}

レスポンスではトランザクションIDがJSON形式で返されます.

以下にcurlコマンドによる実行例を示します.

$ curl -XPOST http://localhost:8080/transactions -d '{"event":"payment","amount":100,"accounts":{"consumer":"ebd26edf-9622-4eb3-44be-96efbf52ce6f","merchant":"c01d1c43-0e36-4680-48cf-53d003fd1732"}}'
{"id":"eadfeec9-74f5-5908-0c05-3d5fec0gh405"}

支払い後の消費者口座と加盟店口座の残高を確認すると,正しく支払いトランザクションが実行されたことがわかります.

$ curl -s http://localhost:8080/accounts/ebd26edf-9622-4eb3-44be-96efbf52ce6f/balance
{"amount":5}
$ curl -s http://localhost:8080/accounts/c01d1c43-0e36-4680-48cf-53d003fd1732/balance
{"amount":98}

取引履歴の閲覧 

これまでの取引の一覧は,以下の方法で確認できます.

GET /transactions

Request Payload:
{account_id: <口座ID>}
Share on FacebookTweet about this on TwitterShare on LinkedInEmail to someone