ちょっと時間が経ったけど、Open Assets Extensions to Payment Requestsにおけるアセットの交渉に関するプロトコルを定義したドキュメントが公開されてたのでざっと訳してみる。
ざっくり言うと、支払い方法(どんなアセットでどんな量の支払いができのか)についてクライアントと売り手の間で支払いの前に事前に調整するプロトコル。
動機
Payment requestsは、クライアントへ正確なトランザクション要件を伝えるのに役立つ。支払いの結果であるpayment responseは支払いに失敗するか、Payment Requestの要件を完全に満たすトランザクションのいずれかになる。
この仕様では、売り手と買い手の間で、これから行おうとしている取引でどんな種類のアセットが利用できるのかといった、支払いの事前の問題を解決する。BIP70ではBitcoinという1つのアセットしかサポートしていないのでこの問題を解決できない。
ここでは3つの新しいタイプのメッセージを紹介する。(BIP70と同様Protocol Bufferによるエンコーディングを採用)
- Payment Method Request
Payment Method Requestには受け入れ可能な支払いオプションに関する情報が含まれ、売り手が顧客にこのメッセージを送る。 - Payment Method
Payment Methodには支払いに使われる正確な支払い情報(種類や、アセットの量など)が含まれ、Payment Method Requestへの応答として顧客が売り手に送信する。その後、売り手は最終的にPayment RequestかPayment Method Rejectionのどちらかのメッセージを送る。 - Payment Method Rejection
Payment Method Rejectionには支払い方法が許容できない理由に関する情報が含まれる。
例
以下ユースケース。
チェックアウト(買い物をカートに入れて決済手段を確定する購入のプロセス)の処理の中で、売り手は顧客にQRコードを表示する。顧客はPayment Method Requestのメッセージを受け取れるクライアントウォレットを使ってQRコードをスキャンする。このメッセージにはこの支払いで、売り手のギフトカード(商品券みたいなものか)とポイントが使えると記載されている。また売り手がチップを受け付けているという情報も含まれている。
クライアントのウォレットは、顧客が利用可能なギフトカードとポイント両方持っていること認識し、顧客にどちらを支払いに使うかもしくは両方使うかの選択をせまる。ここで顧客はギフトカードを選択する。続いてウォレットは顧客にチップを入力するよう求める。顧客はチップを入力し支払いの確認をする。クライアントウォレットはその後Payment Methodを作成し、売り手の端末に返信する。
売り手の端末は受け取ったPayment Methodを使ってPayment Requestメッセージを作成し、クライアントのウォレットに返信する。続いてクライアントのウォレットはトランザクションを生成し、Paymentメッセージに入れ、売り手の端末に送信する。すると売り手の端末に顧客への支払い確認が表示される。
仕様
Payment Method Request
Payment Requestと同様、Payment Method Requestも必要に応じて、売り手のIDと紐付き、X.509証明書で署名される。
message PaymentMethodRequest { optional uint32 payment_details_version = 1 [default = 1]; optional string pki_type = 2 [default = "none"]; optional bytes pki_data = 3; required bytes serialized_payment_method_details = 4; optional bytes signature = 5; }
PaymentMethodRequestの意図と署名の検証ルールはBIP70と同様。
payment_method_details | versioning/upgradingの議論については以下参照。 |
pki_type | 売り手を識別するのにPKIが使用される。全ての実装は"none"、"x509+sha256"、"x509+sha1"の全てをサポートする必要がある。 |
pki_data | 売り手を識別しデジタル署名を作成するのに使われるPKIシステムデータ。X.509証明書の場合、pki_dataには1つ以上のX.509証明書が含まれる。 |
serialized_payment_method_details | Protocol BufferでシリアライズされたPaymentMethodDetailsのメッセージ。 |
signature | PaymentMethodRequestメッセージをProtocol Bufferでシリアライズしたハッシュのデジタル署名。全てのフィールドを番号順にシリアライズしpki_data内の公開鍵に対応する秘密鍵で署名されている。オプションフィールドはシリアライズされない。(ただ、デフォルト値が設定されているフィールドについてはシリアライズされ、署名にも影響する。)またシリアライズする前に、signatureフィールドは必ず空にしておく必要がある。(PaymentMethodRequestの署名時にそのフィールドも含まれてしまうため) |
Payment Method Details
message PaymentMethodDetails { optional string network = 1 [default = "main"]; required string payment_method_url = 2; repeated PaymentItem items = 3; required uint64 time = 4; optional uint64 expires = 5; optional string memo = 6; optional bytes merchant_data = 7; }
network | Bitcoinのプロダクション環境である"main"かテスト環境である"test"のいずれかを指定。クライアントがネットワークからPaymentRequestを受信した場合、そのサポートしていない場合はリクエストをリジェクトする必要がある。 |
payment_method_url | PaymentRequestを取得するためのPaymentMethodメッセージのセキュアな送信先(通常httpsを使用)。 |
items | 異なる支払いの部分(インボイスやチップや寄付とか)を説明するためのPaymentItemオブジェクトのリスト。 |
time | PaymentRequestが作成された時刻(UNIXタイムスタンプ) |
expires | PaymentRequestが無効になる時間(UNIXタイムスタンプ) |
memo | このPaymentMethodRequestについて説明するメモで顧客に表示される。UTF-8でエンコードされたプレーンテキストで特にフォーマットなどは定義されていない。 |
merchant_data | PaymentRequestを識別するために売り手によって使用される任意のデータ。売り手が支払いとPaymentRequestを関連付ける必要がなかったり、PaymentRequestを別々のpayment addressに関連付けたりしている場合は省略可能。 |
Payment Item
各アイテムはタイプを持っており、デフォルトのタイプは"default"。異なるタイプを様々な用途で利用できる。例えば、”tip”というタイプは、チップの追加を顧客に促すために使われ、"donation"というタイプは慈善団体への寄付を顧客に促すのに使われる。しかし"default"以外のタイプは本仕様のスコープ外。
message PaymentItem { optional string type = 1 [default = "default"]; optional bool optional = 2 [default = false]; optional bytes item_identifier = 3; optional uint64 amount = 4 [default = 0]; repeated AcceptedAsset accepted_assets = 5; optional string memo = 6; }
type | アイテムのタイプの標準識別子。クライアントは特定のタイプに関してどう反応すればいいか分からない場合はPaymentItemを無視すべき。 |
optional | このアイテムがオプションかどうかを決定するフラグ。省略した場合、そのアイテムはオプションではなく必須アイテム。 |
item_identifier | 売り手がPaymentMethodのレスポンス内でこのアイテムを識別するために売り手によって使われる任意のデータ。 |
amount | 支払われるユニットの数。利用可能なアセットと交換比率はAcceptedAssetメッセージでカバーされる。量がゼロだったり欠落している場合、クライアントは任意の量を出してもOK。 |
accepted_assets | このアイテムに関する1つ以上のassets acceptable。assets acceptable については↓のAcceptedAsset を参照。 |
memo | インボイスの要約やチャリティの名前などアイテムの説明を人間が読める形式で記載したデータ |
Accepted Asset
message AcceptedAsset { optional string asset_id = 1 [default = "default"]; optional string asset_group = 2; optional double multiplier = 3 [default = 1.0]; optional uint64 min_amount = 4 [default = 0]; optional uint64 max_amount = 5; }
asset_id | アセットのOpen Assets ID。"default" は特殊な値で現在のネットワークのネイティブ通貨を指す。(BitcoinのmainnetであればBTC) |
asset_group | アセットのグループを記述した任意の文字列。クライアントはグループと一致するアセット(Asset Definition Fileやその他の方法によって定義される)を選択することができる。asset_idもasset_groupも未定義の場合はBitcoinを使うものと判断される。 |
multiplier | 支払い金額を満たすために必要な特定のアセットの量を計算するのに使われるコンバージョン率。正確な量は売り手によって計算されPaymentRequest内にセットされるので、この数字は正確な値である必要はなく、UIで表示する目的で使われる。 |
min_amount | 売り手に受け入れられるこのアセットの最小量。 |
max_amount | 売り手に受け入れられるこのアセットの最大量。 |
Payment Method
クライアントが支払い方法を選択すると、payment_method_urlで定義されたURLにPaymentMethodメッセージを返す。
売り手はtypeかitem_identifierのいずれかのフィールドを使って、PaymentItemオブジェクトとPaymentMethodItemを一致させることができる。例えばtypeが"default"の場合は、それだけで識別できるのでitem_identifierを定義する必要は無い。クライアントは必ず対応するPaymentMethodItemオブジェクトにtypeとitem_identifierの両方をコピーする必要がある。
message PaymentMethod { optional bytes merchant_data = 1; repeated PaymentMethodItem items = 2; } message PaymentMethodItem { optional string type = 1 [default = "default"]; optional bytes item_identifier = 2; repeated PaymentMethodAsset payment_item_assets = 3; } message PaymentMethodAsset { optional string asset_id = 1 [default = "default"]; optional uint64 amount = 2; }
merchant_data | PaymentMethodDetails.merchant_dataからコピーされる。売り手がPaymentsとPaymentRequestsを一致させるのに利用するインボイスNoやその他のデータ。悪意のあるクライアントがmerchant_dataを改竄する可能性があるので、売り手だけが持つ秘密鍵等で署名する等、なんらかの認証の仕組みが必要。 |
items | PaymentMethodItemオブジェクトのリストで、対応するPaymentItemの支払いに使われるアセットとその量を記述する。 |
item_identifier | PaymentItem.item_identifierからコピーされる。対応するPaymentItemとPaymentItemMethod を一致させるのに使われる。 |
payment_item_assets | 各payment itemで使われる1つ以上のアセットの説明。 |
payment_item_type | PaymentItem メッセージ内で定義されたpayment itemのタイプ。 |
asset_id | アセットのOpen Assets ID。"default" は特殊な値で現在のネットワークのネイティブ通貨を指す。 |
amount | 支払われる量(オプション)。この値が定義されていた場合は、Payment Requestに必ずamountが定義されている必要があり、定義されていない場合Payment Request にはpayment itemが完全に支払われいることを確認するために任意の量を含めることができる。 |
Payment Method Rejection
もしクライアントがサポートしていないアセットや無効な金額を指定された場合、売り手は支払い方法に関する調整が失敗したことを返信することができる。リジェクションメッセージには指定されたアセットを拒否する理由を含めることができる。
message PaymentMethodRejection { optional string memo = 1; optional uint64 code = 2; repeated PaymentMethodRejectedAsset rejected_assets = 3; } message PaymentMethodRejectedAsset { required string asset_id = 1; optional uint64 code = 2; optional string reason = 3; }
memo | 人間が読める形のリジェクト理由。任意の資産に固有のものではない。 |
rejected_assets | (オプション)アセット毎のリジェクト理由のリスト |
code | (オプション)売り手が定義したエラーコード。クライアントはデバッグやUIに表示するために使用する。 |
asset_id | リジェクトの原因となったアセットのID |
reason | 人間が読める形のアセットをリジェクトした理由 |
MIME types
Payment Methodの調整はPayment Requestsと同じURLで始まる。
Payment Methodを採用するには、クライアントがPayment Request URIにアクセスする際Accept:ヘッダにapplication/oa-paymentmethodrequestを追加する。互換性がある売り手は、Payment Method Requestメッセージに対しContent-type: application/oa-paymentmethodrequestをセットしたレスポンスを返す。
クライアントがOpen AssetsかBitcoinのPayment Requestをサポートしている場合、適切なMIME typesをAccept: listに追加し対応するメッセージが予想される。
クライアントはPayment Method メッセージへの返信に、payment_method_urlに記載されているURLにapplication/oa-paymentrequestを使用する。この時、クライアントは再度Payment Methodが帰ってくるPayment Method Request の受信を避けるため、application/oa-paymentmethodrequestを使ってはいけない。Payment Method のContent-Typeは(リクエスト、レスポンス両方とも)application/oa-paymentmethod。
Payment Method RejectionメッセージのContent−Typeはapplication/oa-paymentmethodrejectionを使う。