There are two things that need to happen for users to successfully complete purchases using Tari Payment Server:
- A payment for at least the amount of the order needs to be made to an authorised hot wallet. The hot wallet does not need to live on the same machine as the payment server, but it must be registered and whitelisted with the payment server.
- We need to link the order number for the purchase to a Tari payment address with a signed message.
This can be done in one, or two steps.
In the usual purchase flow of
Finalise order on storefront --> Pay with Tari --> Claim order
the user knows the order number at the time of payment.
A signed claim message can be provided in the memo
field of the payment transaction. and this will be forwarded
and verified by Tari Payment Server.
This requires some work on the part of the wallet developer, since generating a claim message needs to be done in the wallet transaction process. Generating a claim message is straightforward but not trivial.
The authoritative signature methodology is in the MemoSignature method of the tari_payment_engine
source code.
The code takes priority over any deviations between this document and the code.
When users claim an order, we cannot just believe what users say, because this would let folks attach other wallet addresses to their orders and hope that one day someone makes a payment from that wallet and their order will then be fulfilled.
Users need to prove that they own the wallet address they provide in the order. This is done by signing a message with the wallet's private key. The message is constructed from the wallet address and the order ID (preventing naughty people from using the same signature for their own orders, and again, trying to get free stuff).
The signature is then stored in the order memo field, and the payment server can verify the signature by checking the wallet's public key against the signature.
The message is constructed by concatenating the wallet address and the order ID, separated by a colon. The challenge is a domain-separated Schnorr signature. The full format is:
{aaaaaaaa}MemoSignature.v1.challenge{bbbbbbbb}{address}:{order_id}
where
aaaaaaaa
is the length ofMemoSignature.v1.challenge
, i.e. 25 in little-endian format.bbbbbbbb
is the length ofaddress
(64) +:
(1) +order_id.len()
in little-endian format.address
is the Tari address of the wallet owner, in hexadecimalorder_id
is the order ID, a string
The message is then hashed with Blake2b<U64>
to get the challenge.
You should not implement the above algorithm manually (it is provided here for reference). Instead, use the
MemoSignature::create
method in the tari_payment_engine
crate. This method produces a signature that can be
serialized into JSON and stored in the memo field of the transaction.
A valid memo that contains an order claim has the following requirements:
- The entire memo must be a single JSON object.
- The JSON object must a
claim
key, with the claim object attached. - The claim object must have the following format
address
: The Tari address of the wallet owner, in hexadecimal.order_id
: The order ID being claimed, a string.signature
: The signature of the message constructed from the address and order ID, in hexadecimal.
An example of a valid memo is:
{
"claim": {
"address": "a8d523755de41b9c14de709ca59d52bc1772658258962ef5bbefa8c59082e54733",
"order_id": "oid554432",
"signature": "2421e3c98522d7c5518f55ddb39f759ee9051dde8060679d48f257994372fb214e9024917a5befacb132fc9979527ff92daa2c5d42062b8a507dc4e3b6954c05"
},
"other_stuff(optional)": {
"foo": "bar"
}
}
In this flow, the user pays for the order, and claims the order in two separate steps. The steps can be carried out in any order. This is useful when
- the user does not know the order number at the time of payment,
- when users want to use one-sided payments to the TPS wallet,
- when the user's wallet does not support generating an order claim.
- when the user dows not know where to send funds (in this case, make the claim first, and use the
send_to
field to complete the payment).
This approach can also be used by wallets to integrate TPS if the user flow is better served by a 2-step process.
Make a payment to the TPS wallet. This payment can be made at any time, and will be credited to the address of the sender's wallet.
Make a POST
request to the /order/claim
endpoint of the Tari Payment Server with the order ID and a claim message.
Specifically, the format of the request body must be a claim object as described above. For example,
{
"address": "a8d523755de41b9c14de709ca59d52bc1772658258962ef5bbefa8c59082e54733",
"order_id": "oid554432",
"signature": "2421e3c98522d7c5518f55ddb39f759ee9051dde8060679d48f257994372fb214e9024917a5befacb132fc9979527ff92daa2c5d42062b8a507dc4e3b6954c05"
}
If successful, the response will be a 200 OK
with a JSON object containing the order ID and the status of the claim.
{
"order_id": "oid554432",
"total_price": 50000000,
"expires_at": "2024-06-29T14:00:00Z",
"status": "Claimed",
"send_to": "addressofhotwallet",
}
If unsuccessful, the response will be a 40x
(if a user error) or 50x
(if a backend error) code with a reason for
the failure.
In general, the order id must be obtained at some point during the checkout flow on the supported storefront.
For example, in Shopify, the Order Confirmation modal and email is configured to display a QR code that contains the following schema information:
tari://pay?shop={shopify_store_id}&order_id={order_id}&amount={total_amount_in_microtari}&send_to={hot_wallet_address}
The order confirmation might also capture this information in a QR code for easy scanning by Aurora:
This information is sufficient to complete both the payment and the claim steps as described above.