The JSON Payment Protocol establishes a regular way of communicating about crypto payments.
There is a lot that can go wrong with cryptocurrency payments. When they go wrong, everyone gets a massive headache.
The JSON Payment Protocol was developed to make your life easier, save your company money, and keep your customers happy.
To avoid suffering painful financial losses and massive headaches, the JSON Payment Protocol gives wallets and payment processors an easy way to communicate directly with one another and ensure that every payment is perfect every time.
Traditional wallet payments typically involve one address sending to another address. But so much can go wrong here. Even if the sender has the correct address, he may use too low of a fee to confirm in a reasonable amount of time. He may send too much or too little. For businesses, this is unacceptable. Real businesses with transactions at scale need precision in payments.
That's why the Payment Protocol uses URIs instead of addresses. This offers many benefits:
The modern Payment Protocol uses everyone's favorite format, JSON, to share information about:
In the past, if wallets wanted to communicate directly with processors, they had to use protocol buffers to serialize data into binary. Yuck!
To start reducing your user errors to zero, follow these steps:
It looks like pay:?r=https://api.anypayx.com/r/VaGkciAzT
For native currencies such as DASH, XMR, BTC use the same value for both chain and currency.
For token currencies such as USDC or USDT specif the underlying chain ie. ETH, SOL, MATIC, etc.
Here the wallet may prompt their user to choose from the available list of payment options, or it may choose for them depending on your application logic.
Payment details include one or more outputs required by the payment. Often applications will require the wallet to pay to more than one output in a single transaction as is standard practice UTXO blockchains and some smart contract transactions. Most Anypay payment requests include multiple outputs for UTXO chains and all outputs must be present upon validation.
(Server) Verifies invoice exists and is still accepting payments, responds with payment-request
(Client) Generates a payment to match instructions on payment-request
(Server) Validates invoice exists and is still accepting payments
(Server) Validates payment matches address, amount, and currency of invoice and has a reasonable transaction fee.
(Server) Notifies client payment is accepted and broadcasts to p2p network
(Client) Sends payment to server and broadcasts to p2p network
(Server) Validates signed payment and broadcasts payment to network.
In general, the payment should not be broadcast by the client. If the payment is rejected by the server, your client MUST NOT BROADCAST the payment. Broadcasting a payment before getting a success notification back from the server will lead to a failed payment for the sender in most cases. If the wallet broadcasts a payment that was rejected by the payment server there is no way to recover the funds without annoyingly contacting the merchant.
A GET request should be made to the payment protocol url:
/i/<invoiceId>
Accept: application/payment-options
X-Paypro-Version: 2
A list of payment options will be returned.
{
"time":"2021-06-02T14:25:47.205Z",
"expires":"2021-06-02T14:40:47.205Z",
"memo":"Payment request for Anypay invoice PfCwZLxWctSrdgYcnJM8G8 for merchant Test Organization",
"paymentUrl":"https://anypayx.com/i/PfCwZLxWctSrdgYcnJM8G8",
"paymentId":"PfCwZLxWctSrdgYcnJM8G8",
"paymentOptions":[
{
"chain":"BCH",
"currency":"BCH",
"network":"main",
"estimatedAmount":33100,
"requiredFeeRate": 1
"decimals":8,
},
{
"chain":"BTC",
"currency":"BTC",
"network":"main",
"estimatedAmount":33100,
"requiredFeeRate":41.915
"decimals":8,
},
{
"chain":"XMR",
"currency":"XMR",
"network":"main",
"estimatedAmount":1427800,
"requiredFeeRate":1,
"decimals":12,
},
{
"chain":"ETH",
"currency":"ETH",
"network":"main",
"estimatedAmount":3660000000000000,
"requiredFeeRate":36500000000,
"decimals":18,
},
{
"chain":"ETH",
"currency":"USDC",
"network":"main",
"estimatedAmount":1000,
"requiredFeeRate":36500000000,
"decimals":6,
"selected":false
},
{
"chain":"SOL",
"currency":"USDT",
"network":"main",
"estimatedAmount":10000000000000000000,
"requiredFeeRate":36500000000,
"decimals":6,
"selected":false
},
{
"chain":"MATIC",
"currency":"USDC",
"network":"main",
"estimatedAmount":10000000000000000000,
"requiredFeeRate":36500000000,
"decimals":6,
"selected":false
},
{
"chain":"ETH",
"currency":"USDC",
"network":"main",
"estimatedAmount":10000000,
"requiredFeeRate":36500000000,
"decimals":6,
"selected":false
},
{
"chain":"XRP",
"currency":"XRP",
"network":"main",
"estimatedAmount":9822618,
"requiredFeeRate":12,
"decimals":6,
"selected":false
},
{
"chain":"DOGE",
"currency":"DOGE",
"network":"main",
"estimatedAmount":2412370600,
"requiredFeeRate":958724.946,
"decimals":8,
"selected":false
}
{
"chain":"SOL",
"currency":"SOL",
"network":"main",
"requiredFeeRate":36500000000,
"minerFee":0,
"decimals": 18,
"selected":false
}
]
}
time
- ISO Date format of when the invoice was generatedexpires
- ISO Date format of when the invoice will expirememo
- A plain text description of the payment request, can be displayed to the user / kept for recordspaymentUrl
- The url where the payment should be sentpaymentId
- The invoice ID, can be kept for recordspaymentOptions
- An array of payment options. Each option includes the chain and currencyEach payment option includes:
chain
- The chain that the transaction should be valid oncurrency
- The currency on a given chain that the trasaction should be denominated innetwork
- The network that the transaction should be valid onestimatedAmount
- Amount of currency units required to pay this invoicedecimals
- Number of decimal places the currency usesA POST request should be made to the payment protocol url with {chain,currency}:
/i/<invoiceId>
Content-Type: application/payment-request
DO NOT use the standard
Content-Type: application/json
DO NOT include anAccept
property
chain
- a chain that was present in the payment-options responsecurrency
- (optional) the particular currency on the chain you will pay with. Defaults to chain{
"chain": "<chain 3 letter code>",
"currency": "<optional (ERC20) 3 letter code>",
}
{
"time": "2023-04-21T03:07:43.495Z",
"expires": "2023-04-21T03:22:43.495Z",
"memo": "",
"paymentUrl": "https://api.anypayx.com/i/0ZJZRNRVM",
"paymentId": "0ZJZRNRVM",
"chain": "BTC",
"currency": "BTC",
"network": "main",
"instructions": [
{
"type": "transaction",
"requiredFeeRate": 107,
"outputs": [
{
"address": "19T4miK5CUSLcYQDYSUK9T2jkLUipLhh8g",
"amount": 7000
}
]
}
]
}
{
"time": "2019-06-13T18:35:19.138Z",
"expires": "2019-06-13T18:50:19.138Z",
"memo": "Payment request for Anypay invoice TiXuyEmcJRCcinoFoY3Cym for merchant Test Account",
"paymentUrl": "https://test.anypayx.com/i/TiXuyEmcJRCcinoFoY3Cym",
"paymentId": "TiXuyEmcJRCcinoFoY3Cym",
"chain": "BCH",
"network": "test",
"instructions": [
{
"type": "transaction",
"requiredFeeRate": 1,
"outputs": [
{
"amount": 239200,
"address": "qzpxcqxw8d3pylyde8283u6yeeaug46fe5nl6d9t4f"
},
{
"amount": 50100,
"address": "qqtmrylvxkt7vjs9gn2uj6tclwphlrgv5g0r68w7k8"
}
]
}
]
}
The following coins are in private beta and scheduled for rollout in the coming few weeks. Exactly response formats are subject to change.
{
"time": "2019-06-13T18:33:00.827Z",
"expires": "2019-06-13T18:48:00.827Z",
"memo": "Payment request for Anypay invoice S6cxqqUNUcMV41dfBS8XHn for merchant Test Account",
"paymentUrl": "https://test.anypayx.com/i/S6cxqqUNUcMV41dfBS8XHn",
"paymentId": "S6cxqqUNUcMV41dfBS8XHn",
"chain": "ETH",
"network": "test",
"instructions": [
{
"type": "transaction",
"value": 3836000000000000,
"to": "0x37d7B3bBD88EFdE6a93cF74D2F5b0385D3E3B08A",
"data": "0xb6b4af05000000000000000000000000000000000000000000000000000da0d2595bc000000000000000000000000000000000000000000000000000000000174876e8000000000000000000000000000000000000000000000000000000016b55554827c63ba4a65dfaf06093315d08f3240fdd0724409e09ea250226f656964dcb44d17a565f7590c67cdb3241eb969f1c24db402ef5714e822a574afa8b6802a2a4ca000000000000000000000000000000000000000000000000000000000000001c456e391b0b2ef6d8d130fdff97085189eecc10f3f78f2b5cdaed24609ca89fd42aafed00dccb025ba383d4afecbf136b6a2e480620bb2c30f8d5a3e71d7f88090000000000000000000000000000000000000000000000000000000000000000",
"gasPrice": 100000000000
}
]
}
On a successful request, the response will contain the following headers.
digest
- A SHA256 hash of the JSON response string, should be verified by the client before proceedingx-identity
- An identifier to represent which public key should be used to verify the signature. For example, for Anypay's ECC keys, we will include the public key hash in this header. Implementations should NOT include the public key here directly.x-signature-type
- The signature format used to sign the payload. For the foreseeable future Anypay will always use ECC
. However, we wanted to grant some flexibility to the specification.x-signature
- A cryptographic signature of the SHA256 hash of the payload. This is to prove that the payment request was not tampered with before being received by the wallet.time
- ISO Date format of when the invoice was generatedexpires
- ISO Date format of when the invoice will expirememo
- A plain text description of the payment request, can be displayed to the user / kept for recordspaymentUrl
- The url where the payment should be sentpaymentId
- The invoice ID, can be kept for recordschain
- Three letter code for the chain these instructions are valid forcurrency
- Optional, Three letter code for the token these instructions are valid forinstructions
- An array of instructions that can be used to construct transactions that will fufill this paymentNow that the server has told the wallet that its payment is acceptable, the wallet can send the fully signed transaction.
A POST request should be made to the payment protocol url with {chain,transactions,currency}
:
/i/<invoiceId>
Content-Type: application/payment
chain
- a chain that was present in the payment-options response
transaction
- array contening the signed transaction as well as the weighted size
currency
- (optional) the particular currency on the chain you will pay with (for ERC20 tokens)
{
"chain": "<chain 3 letter code>",
"transactions": "<{tx: string, weightedSize?: number}>",
"currency": "<optional (ERC20) 3 letter code>",
}
{
"chain": "XRP",
"currency": "XRP",
"transactions": [
{
"tx": "120000228000000024000000095011F6751F266C7E664CB3CDAE77D091ED73E3D365591D8FA769BEA9F694C5C4A5DF614000000000448E1768400000000000000C7321030834A2B07BD552337FEA00C32E8DDDDB541BC7353DA7E982B4C191BFE432D3BE74473045022100A2D1E2A0B0E01EAAA36F67F650463391E4390F9F540D4C7553E1698F866A16E002201C5CB68137805ECC73F2D4459E7840842C255065A716475CDDFCA007EA570A1581148FF291E50F16A206B96D383E1F86CC47E21727DA8314C5B8A782192BFF4D8FF629A88BEEB417A4D9EEB2"
}
]
}
The response will be a JSON format payload containing the original payment body and a memo field which should be displayed to the user.
{
"payment": {
"transactions": [
{
"tx": "120000228000000024000000095011F6751F266C7E664CB3CDAE77D091ED73E3D365591D8FA769BEA9F694C5C4A5DF614000000000448E1768400000000000000C7321030834A2B07BD552337FEA00C32E8DDDDB541BC7353DA7E982B4C191BFE432D3BE74473045022100A2D1E2A0B0E01EAAA36F67F650463391E4390F9F540D4C7553E1698F866A16E002201C5CB68137805ECC73F2D4459E7840842C255065A716475CDDFCA007EA570A1581148FF291E50F16A206B96D383E1F86CC47E21727DA8314C5B8A782192BFF4D8FF629A88BEEB417A4D9EEB2"
}
]
},
"memo": "Transaction received by Anypay. Invoice will be marked as paid if the transaction is confirmed."
}
Mime | Description |
---|---|
application/payment-options |
Retrieve the options that the invoice can be paid with, this is specified with the Accept header. |
application/payment-request |
Associated with the server's payment request, this specified on the client Accept header when retrieving the payment request |
application/payment |
Used by the client when sending their proposed payment transaction payload |
Coins based on bitcoin and similar chains use a structure called unspent outputs to transfer coins from one account to another. Transactions almost always contain more than one output which directs coins to different parties including but not limited to change back to the sender.
Coins based on the ethereum virtual machine or other account-based tokens do not have "outputs" in their instructions. Rather instructions contain a "data" propery which contains the virtual machine instructions required to execute a transfer.
Token coins are assets that are managed and transfer through the use of smart contracts, or small programs embedded in the blockchain. Payment requests for token transfers also contain "data" as the instructions, rather than outputs.