With PayID, you can use human-readable addresses to transmit value in the currency of your choice.
The PayID network allows participants to reach one another through a standardized address called a Payment Pointer. For example, $wallet.com/alice maps to the corresponding URL https://wallet.com/alice.
Make an HTTP GET request that follows the PayID protocol, and it resolves to an address on the underlying payment network. PayID is a payment-network-agnostic protocol, capable of sending payments in BTC, ETH, XRP, or fiat networks like ACH.
Each request must have a valid Accept request HTTP header. For example: Accept: application/xrpl-mainnet+json indicates the XRP Ledger Mainnet payment network and the JSON format of the response payload.
- Web standards
- Extensions and Travel Rule compliance
- PayID integration and the Private PayID API
- Query users and manage payments with the PayID Public API
PayID uses existing web standards and infrastructure, including HTTP, DNS, and Payment Pointers. Each institution supports its users through its domain, creating a decentralized network where institutions maintain sovereignty over their own users. Each institution is empowered to participate in the network by deploying its own server with its own users. Additionally, a user who prefers to self-custody their cryptocurrency can deploy a personal PayID server, as described in Set up a PayID server.
PayID does not rely on any consensus protocols, greatly simplifying infrastructure demands.
PayID is built on the most successful decentralized network in history: the web. PayID is fully peer-to-peer with no central counterparty. Anyone can set up their own personal PayID server so they have no counterparty risk.
PayID fully abstracts away underlying payment rail details from end users. A user only needs to keep track of a single PayID rather than a complex address for each network. Service operators can manage the underlying addresses behind the scenes according to their own security and privacy best practices without requiring the end user to take any action.
PayID is a highly extensible and flexible open standard. Of relevance for many crypto services, PayID can be extended to provide a simple solution for Travel Rule compliance. Financial institutions can communicate regulatory requirements and the corresponding necessary data using PayID's standard REST API.
PayID improves the user experience of sending cryptocurrency payments, and it also provides a platform for present-day features like Travel Rule compliance and future features like subscriptions and pull payments. To achieve mainstream cryptocurrency adoption, we must build on top of PayID and other systems that integrate well with traditional financial infrastructure.
PayID provides an abstraction layer so that users can send payments to human-readable addresses while virtual asset service providers (VASPs) can meet the necessary compliance requirements in the background.
Coming soon: When a payment is sent to a payment pointer, if both entities are VASPs and must comply with the Travel Rule, they will exchange the user data in a secure handshake.
Features and integrations on the roadmap include:
- Invoices and receipts
- Additional payment rail standards
- Payment request (W3C Standard) integration
- Advanced payment type support: pull, push, streaming, subscription
- PayID forwarding and redirection
You can deploy your own PayID server and then create PayIDs for your users using the Private PayID API. You can also query and modify this list of users. This API should be exposed internally only, so that only your company's systems can update PayID mappings.
Once you have set up a PayID server, anyone can use the PayID Public API to query address information. This API is publicly accessible so that anyone can send payments to your users' PayID addresses.
Participating institutions can use Xpring’s open source reference implementation of the PayID server. You might be able to ease the deployment process by connecting your PayID server to your existing user database so that it can respond to incoming GET requests.
To set up your own demo server, first ensure you have Docker installed, and then run these commands. You will create a local docker image.
git clone [email protected]:xpring-eng/payid.git
cd payid
./demo/run_payid_demo.sh
You can also set up your PayID server using [AWS and NGINX][aws-deploy.md].
You can then use the Private PayID API to:
- Create a user
- Get user information
- Modify a user
- Delete a user
For demonstration purposes, once you have set up your PayID server, you can access the Private PayID API using these cURL commands.
Get user information for an existing user
curl --location --request GET 'http://127.0.0.1:8080/hbergren' \
--header 'Accept: application/xrpl-testnet+json'
Create a user
First, run this command to create a user.
curl --location --request POST 'http://127.0.0.1:8081/v1/users' \
--header 'Content-Type: application/json' \
--data-raw '{
"payment_pointer": "$127.0.0.1/dino",
"addresses": [
{
"payment_network": "XRPL",
"environment": "TESTNET",
"details": {
"address": "T7WSFgh6owANWoD2V3WRg6aeBveBzExkpDowirvnLDGL2YW"
}
}
]
}'
Then, run this command to get information about the newly-created user.
curl --location --request GET 'http://127.0.0.1:8080/dino' --header 'Accept: application/xrpl-testnet+json'
When you have your open source server set up, you can create and manage users with the Private PayID API.
Substitute your own domain for {{http(s)}}{{host}}. If running locally, you can use http://127.0.0.1:8081/.
Use /v1.
This operation creates a single user.
POST {{http(s)}}{{host}}/v1/users
Payload: Single user schema
201 Created
No response body.
See Example error schema.
| Code | Description |
|---|---|
| 400 | Bad Request |
| 409 | Conflict |
| 503 | Service Unavailable |
curl --location --request POST '127.0.0.1/v1/users' \
--header 'Content-Type: application/json' \
--data-raw '{
"payment_pointer": "$127.0.0.1/dino",
"addresses": [
{
"payment_network": "XRPL",
"environment": "TESTNET",
"details": {
"address": "T7WSFgh6owANWoD2V3WRg6aeBveBzExkpDowirvnLDGL2YW"
}
}
]
}'
You can query information about an existing user with the following request.
GET {{http(s)}}{{host}}/v1/users/{:$payment_pointer}
Example: GET http://127.0.0.1:8081/v1/users/$xpring.money/hbergren
200 OK
The user that matches the specified payment pointer is returned. See Example single user schema.
See Example error schema.
| Code | Description |
|---|---|
| 400 | Bad Request |
| 404 | Not Found |
| 503 | Service Unavailable |
curl --location --request GET '{{payid_private_host}}/v1/users/${{payid_private_host_no_transport}}/dino' \
--header 'Content-Type: application/json'
You can modify the user information associated with a particular payment pointer.
PUT {{http(s)}}{{host}}/v1/users/{:$payment_pointer}
The request payload is the modified user schema for the specified payment pointer.
200 OK
201 Created
The response body is the updated user schema.
| Code | Description |
|---|---|
| 400 | Bad Request |
| 404 | Not Found |
| 409 | Conflict |
| 503 | Service Unavailable |
curl --location --request PUT '127.0.0.1/v1/users/${{payid_private_host_no_transport}}/dino' \
--header 'Content-Type: application/json' \
--data-raw '{
"payment_pointer": "$127.0.0.1/dina",
"addresses": [
{
"payment_network": "XRPL",
"environment": "TESTNET",
"details": {
"address": "T7WSFgh6owANWoD2V3WRg6aeBveBzExkpDowirvnLDGL2YW"
}
}
]
}'
{{http(s)}}{{host}}{{private_port}}/v1/users/{:payment_pointer}
200 OK
| Code | Description |
|---|---|
| 400 | Bad Request |
| 404 | Not Found |
| 503 | Service Unavailable |
curl --location --request DELETE 'https://***REMOVED***/v1/users/$***REMOVED***/dino'
These schemas are used for request or response payloads for various requests.
A single user can have multiple destinations, because the same user can have addresses on different networks for different currencies. This schema is used as a payload with a request to create a user, or as a response to a request to get information about a user.
{
"payment_pointer": "$xpring.money/johndoe",
"addresses": [
{
"payment_network": "XRPL",
"environment": "TESTNET",
"details": {
"address": "TVQWr6BhgBLW2jbFyqqufgq8T9eN7KresB684ZSHKQ3oDth"
}
}, {
"payment_network": "XRPL",
"environment": "MAINNET",
"details": {
"address": "X7zmKiqEhMznSXgj9cirEnD5sWo3iZSbeFRexSFN1xZ8Ktn"
}
}
]
}| Field | Description |
|---|---|
payment_pointer |
Payment pointer address |
addresses |
Object that includes payment address information for one or more payment networks. |
addresses.payment_network |
A payment network, like the bitcoin network, the XRPL, or ACH. |
addresses.environment |
"Environment" of the payment network for this payment address. For example, the XPRL has MAINNET, TESTNET, and DEVNET. |
address.details |
Actual payment information for this address. Must be in the form CryptoAddressDetails or AchAddressDetails. |
This example shows the format of an error payload.
{
"statusCode": 422,
"error": "Unprocessable Entity",
"message": "Your data is bad"
}
PayID is a fundamentally neutral protocol. When you make a request, the HTTP Accept header of the request specifies the payment network and environment, and PayID is therefore capable of returning a user's address information for any network in which that user participates.
An example request has this form.
GET /user HTTP/1.1
HOST: wallet.com
The different header options are shown here, with example values.
| Currency | Header | Response |
|---|---|---|
| BTC | application/btc+json | { address: '1BvBMSEYstWetAu4m4GFg7xJaNVN2' } |
| XRP | application/xrpl-mainnet+json | { address: 'XV5sbjUmgPpvXv4ixFWZ5ptAYZ6PD28Sq49uo34VyjnmK5H' } |
| ACH | application/ach+json | { account: '363023456079', routing: '011302838' } |
| ILP | application/spsp4+json | { destination_account: 'g.institution.alice', shared_secret: 'Anofne0Slxjv0Aj4malghem' } |
This example demonstrates a payment to https://institution.com/alice.
const axios = require('axios')
const btc = require('bitcoinjs-lib')
async function pay(user, amount) {
const { address } = await axios.get({
method: 'GET',
baseUrl: 'https://' + user
headers: { accept: 'application/btc+json' }
}
const tx = await new btc.Transaction(address, amount)
}
pay('$institution.com/alice ', 0.63)
In this example, you generate a seed-based wallet using the Xpring SDK. See Xpring SDK docs.
// Xpring SDK
const { Wallet } = require("xpring-js");
const wallet = Wallet.generateWalletFromSeed("snRiAJGe3zRwiYDBm1M");
const tx = await wallet.send(0.63, '$alice.institution.com');The PayID Public API does not require authentication, as it is open to any user. The PayID Private API is meant for administrators who are building a payment network.
The PayID Public API treats a payment pointer as the base endpoint for all of the following requests. Therefore, if you are requesting from $wallet.com/alice the following paths would be:
GET https://wallet.com/alice/
Substitute the appropriate URL for your payment pointer.
You can get payment information for a payment pointer.
Header: `Accept: <request header>`
GET <payment-pointer-url>/
200 OK
A PaymentInformation object is returned.
| Code | Description |
|---|---|
| 404 | Not Found Occurs if payment pointer does not exist. |
| 503 | Service Unavailable |
Interface PaymentInformation {
addressDetailsType: string
addressDetails: CryptoAddressDetails || AchAddressDetails
proofOfControlSignature?: string
paymentPointer?: string // not to be included in unauthenticated GET
} // should be included otherwise
Interface CryptoAddressDetails {
address: string
tag?: string
}
Xpring SDK can be used to simplify the process of developing with PayID. Currently only the Node.js version is available, and Java and Swift will soon be available.
This demo uses PayID to resolve an address on the specified payment network, which is XRP Ledger in this case.
To execute this demo:
- Clone the Xpring SDK repo.
- Change to the
Xpring-SDK-Demo/nodedirectory. - Run
npm ito install the dependencies. - Run
node src/index-payid.jsto execute the demo.
View index-payid.js.
Interface AchAddressDetails {
accountNumber: string
routingNumber: string
}
Interface Error {
statusCode: integer
error: string
message: string
}
This launch of PayID includes those headers specific to the Xpring ecosystem. Each payment network is free to establish its own standard headers. These headers should be submitted with every GET request, but not POST.
| Accept header | Description |
|---|---|
| application/xrpl-mainnet+json | Returns mainnet xAddress |
| application/xrpl-testnet+json | Returns testnet xAddress |
| application/xrpl-devnet+json | Returns devnet xAddress |
| Accept header | Description |
|---|---|
| application/ach+json | Returns account and routing number |
| Accept header | Description |
|---|---|
| application/spsp4+json | Returns destination address and shared secret |

