diff --git a/packages/documentation/README.md b/packages/documentation/README.md
index 824def64ee..2601440e1c 100644
--- a/packages/documentation/README.md
+++ b/packages/documentation/README.md
@@ -61,7 +61,6 @@ Refer to the Starlight documentation on [authoring content](https://starlight.as
We have extracted some commonly repeated patterns within the documentation pages into custom docs components that can be reused. There are components which are shared across all our Starlight documentation sites and those which are specific to this project only. This will determine what the import path is.
- CodeBlock (Shared)
-- Disclosure (Shared)
- Hidden (Shared)
- LargeImg (Shared)
- LinkOut (Shared)
@@ -69,10 +68,10 @@ We have extracted some commonly repeated patterns within the documentation pages
- StylishHeader (Shared)
- Tooltip (Shared)
-For the shared components, if you are using both `CodeBlock` and `Disclosure` on the same page, you can import them both like so:
+For the shared components, if you are using both `CodeBlock` and `LinkOut` on the same page, you can import them both like so:
```jsx
-import { CodeBlock, Disclosure } from '@interledger/docs-design-system'
+import { CodeBlock, LinkOut } from '@interledger/docs-design-system'
```
For more information about importing things in Javascript, please refer to [import on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import).
diff --git a/packages/documentation/astro.config.mjs b/packages/documentation/astro.config.mjs
index 9041f10dec..3838c40783 100644
--- a/packages/documentation/astro.config.mjs
+++ b/packages/documentation/astro.config.mjs
@@ -256,9 +256,9 @@ export default defineConfig({
],
plugins: [
starlightLinksValidator({
- errorOnLocalLinks: false,
- }),
- ],
+ errorOnLocalLinks: false
+ })
+ ]
}),
GraphQL({
schema: '../backend/src/graphql/schema.graphql',
diff --git a/packages/documentation/src/content/docs/integration/playground/overview.mdx b/packages/documentation/src/content/docs/integration/playground/overview.mdx
index cdc5b4cb24..f078cdb9ef 100644
--- a/packages/documentation/src/content/docs/integration/playground/overview.mdx
+++ b/packages/documentation/src/content/docs/integration/playground/overview.mdx
@@ -178,6 +178,7 @@ You can either trigger the debugger by adding `debugger` statements in the code
#### Debugging with VS Code:
To debug with VS Code, add this configuration to your `.vscode/launch.json`:
+
```json
{
"name": "Attach to docker (cloud-nine-backend)",
@@ -220,7 +221,7 @@ The following are the most commonly used commands:
| pnpm localenv:compose up | Start (with TigerBeetle) |
| pnpm localenv:compose up -d | Start (with TigerBeetle) detached |
| pnpm localenv:compose down | Down (with TigerBeetle) |
-| pnpm localenv:compose down --volumes | Down and kill volumes (with TigerBeetle)
+| pnpm localenv:compose down --volumes | Down and kill volumes (with TigerBeetle) |
| pnpm localenv:compose down --volumes --rmi all | Down, kill volumes (with TigerBeetle) and images |
| pnpm localenv:compose:psql config | Show all merged config (with PostgreSQL) |
| pnpm localenv:compose build | Build all the containers (with TigerBeetle) |
@@ -292,4 +293,4 @@ test.rafiki.viXmy1OVHgvmQakNjX1C6kQMri92DzHeISEv-5VzTDuFhrpsrkDzsq5OO9Lfa9yed0L2
#### TigerBeetle container exists with code 137
-There is a known issue when running TigerBeetle in Docker. The container exits without logs and simply shows error code 137. To fix this, increase the Docker memory limit. If you run the local Docker playground on a Windows machine via the Windows Subsystem for Linux (WSL), you can increase the memory limit by configuring your `.wslconfig` file.
\ No newline at end of file
+There is a known issue when running TigerBeetle in Docker. The container exits without logs and simply shows error code 137. To fix this, increase the Docker memory limit. If you run the local Docker playground on a Windows machine via the Windows Subsystem for Linux (WSL), you can increase the memory limit by configuring your `.wslconfig` file.
diff --git a/packages/documentation/src/content/docs/integration/requirements/idp.mdx b/packages/documentation/src/content/docs/integration/requirements/idp.mdx
index 4c839264d7..8f56c4beb3 100644
--- a/packages/documentation/src/content/docs/integration/requirements/idp.mdx
+++ b/packages/documentation/src/content/docs/integration/requirements/idp.mdx
@@ -12,80 +12,71 @@ import {
An identity provider (IdP) is a system or service that stores and manages user identity information, authentication, and consent. Examples of IdPs include OpenID Connect and Okta.
-Integration with an IdP is required if you plan to support outgoing payments via Open Payments. The Open Payments standard requires interactive outgoing payment _grant_ requests, which precede outgoing payment requests. In an interactive grant request, explicit interaction by an individual (e.g., the client's end-user) is required to approve the grant. An example of an interaction is an end-user tapping _Approve_ in an app to authorize a payment.
+Open Payments requires any authorization server that issues interactive grants be integrated with an IdP. Interactive grants are used to gather consent. More information about interactive grants is available [below](#interactive-grants).
-Your IdP will:
+Responsibilities of your IdP include:
-- Authenticate requests from clients, such as mobile apps, to create quotes and payments on Rafiki's backend
-- Facilitate interactions with the client's end-user to gather consent
+- Providing an interface to gather end-user consent for a particular action
+- Sending the interaction choice (approve or deny) to the authorization server
+- Sending a request to the authorization server to finish the interaction
+- Redirecting the user after the interaction is complete
:::note
-We provide Ory Kratos, a cloud-based user management system, for the identity and user management of your [Rafiki Admin](/admin/admin-user-guide) users. Kratos is for internal use only and **cannot** be used as your client-facing IdP.
+We provide Ory Kratos, a cloud-based user management system, for the identity and user management of your Rafiki Admin users. Kratos is for internal use only and **cannot** be used as your IdP for Open Payments.
:::
-### Interactions and consent
+## Interactive grants
-Before an outgoing payment is created via Open Payments, an outgoing payment _grant_ must be issued.
+In Open Payments, grants are used to indicate a resource owner, such as an account holder, has given a piece of software, such as a mobile app, permission (consent) to act on their behalf.
-Outgoing payment grant requests must be interactive. This means the request requires explicit interaction, often from the a client's end-user, to gather consent (permission) before creating the outgoing payment. The interaction is facilitated by your IdP.
+Rafiki's implementation of an Open Payments authorization server requires that consent is collected via an interactive grant before an outgoing payment request is issued. A grant is interactive when explicit interaction by a resource owner (e.g., the software's end user) is required to approve or deny the grant. Tapping an _Approve_ button to authorize a payment is an example of an explicit interaction.
-Your IdP:
+Interactive grants can be optional for incoming payments and quotes; however, they're enabled by default in Rafiki (the `LIST_ALL_ACCESS_INTERACTION` environment variable is `true`). When a grant request includes a `list-all` action for incoming payments and quotes, the request requires interaction. The `list-all` action is used when the client asks to list resources that it did not create.
-
+If `LIST_ALL_ACCESS_INTERACTION` is `false`, you can still force interactive grants for quotes and/or incoming payments by setting the respective variable(s) to `true`.
-1. Provides an interface to gather consent (for example, a consent screen)
-2. Sends the interaction choice (accept/deny) to your authorization server
-3. Sends a request to your authorization server to finish the interaction
-4. Redirects the user after the interaction is complete
+- `QUOTE_INTERACTION`
+- `INCOMING_PAYMENT_INTERACTION`
-
+See the Open Payments documentation for more information on grant negotiation and authorization.
-### Authorization server
+## Authorization servers
-The purpose of an Open Payments authorization server is to grant permission to clients to access the Open Payments APIs. These APIs are used to create incoming payments, quotes, and outgoing payments against an account holder's account.
+Authorization servers grant permission to clients to access the Open Payments Resource APIs. This enables clients to create incoming payments, quotes, and outgoing payments against an account holder's account.
-Rafiki's [auth service](/integration/services/auth-service) provides you with a reference implementation of an Open Payments authorization server. You can use the service as an alternative to developing your own in-house service.
+Rafiki's [auth service](/integration/services/auth-service) provides you with a reference implementation of an Open Payments authorization server. We recommend you use the `auth` service rather than developing your own in-house solution.
-The authorization server extends an HTTP API for your IdP to use to start and finish interactions, collect authorization, get information about a grant, and communicate whether an end-user has authorized a grant. The API's [endpoints](#interaction-endpoints) are described below.
+Rafiki's authorization server also extends an [API](#interaction-endpoints) that provides interaction endpoints for your IdP.
-## Environment variables
+### Environment variables
-The following `backend` variables must be configured on your authorization server.
+The following variables must be configured for the `auth` service.
-| Variable | Helm value name | Default | Description | Required |
-| ------------------------------ | ---------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
-| `IDENTITY_SERVER_URL` | `auth.identityServer.domain` | N/A | Your IdP server's URL where your authorization server will direct clients to so end-users can complete the interaction and authorize a grant. | Y |
-| `IDENTITY_SERVER_SECRET` | `auth.identityServer.secret` | N/A | A shared secret between your authorization and IdP servers that your authorization server will use to secure its IdP-related endpoints.
When your IdP sends requests to your authorization server, your IdP must provide the secret via an [`x-idp-secret`](#x-idp-secret-header) header. | Y |
-| `INCOMING_PAYMENT_INTERACTION` | `auth.interaction.incomingPayment` | `false` | Indicates whether incoming payments grant requests are interactive. | Y |
-| `INTERACTION_EXPIRY_SECONDS` | `auth.interactionExpirySeconds` | `600` | The time in seconds for which a user can interact with a grant request | Y |
-| `INTERACTION_PORT` | `auth.port.interaction` | `3009` | The port number for the [interaction endpoints](#interaction-endpoints) | Y |
-| `INTROSPECTION_PORT` | `auth.port.introspection` | `3007` | The port number of your Open Payments authorization token introspection server | Y |
-| `LIST_ALL_ACCESS_INTERACTION` | N/A | `true` | Specifies whether grant requests including a `list-all` action should require interaction. In these requests, the client asks to list resources that they themselves did not create. | Y |
+| Variable | Helm value name | Default | Description |
+| ------------------------------ | ---------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --- |
+| `IDENTITY_SERVER_URL` | `auth.identityServer.domain` | N/A | The URL of your IdP's server, used by the authorization server to inform an Open Payments client of where to redirect the end-user to start interactions. | Y |
+| `IDENTITY_SERVER_SECRET` | `auth.identityServer.secret` | N/A | A shared secret between the authorization server and the IdP server; the authorization server will use the secret to secure its IdP-related endpoints.
When the IdP server sends requests to the authorization server, the IdP server must provide the secret via an [`x-idp-secret`](#x-idp-secret-header) header. | Y |
+| `INCOMING_PAYMENT_INTERACTION` | `auth.interaction.incomingPayment` | `false` | Indicates whether incoming payments grant requests are interactive. | Y |
+| `INTERACTION_EXPIRY_SECONDS` | `auth.interactionExpirySeconds` | `600` | The time in seconds for which a user can interact with a grant request | Y |
+| `INTERACTION_PORT` | `auth.port.interaction` | `3009` | The port number for the [interaction endpoints](#interaction-endpoints) | Y |
+| `LIST_ALL_ACCESS_INTERACTION` | N/A | `true` | Specifies whether grant requests including a `list-all` action should require interaction. In these requests, the client asks to list resources that they themselves did not create. | Y |
+| `QUOTE_INTERACTION` | `auth.interaction.quote` | `false` | When `true`, quote grants are interactive. |
-## Manage grants
+## Interaction endpoints
-After a pending grant request is created, your IdP server can use the interaction endpoints listed below to:
+The authorization server provided by Rafiki's `auth` service extends an API for an IdP server to use after a pending grant request is created.
-- Start and finish interactions
-- Collect authorization
-- Get information about a grant
-- Communicate whether an end-user has authorized a grant
+Each interaction with an endpoint is identified by an `id` and a `nonce`. Both are provided as query parameters when the authorization server redirects to the IdP server.
-Each interaction is identified by an `id` and a `nonce`. Both are provided as query parameters when your authorization server redirects to your IdP server.
+The endpoints are tied to the auth server URL. For example, if your auth server URL is `https://auth.wallet.example.com`, then calling the `/interact/{id}/{nonce}` endpoint to start a user interaction session would look as follows:
-The endpoints are appended to the `IDENTITY_SERVER_URL` you defined when configuring your [environment variables](#environment-variables).
-
-
-
-```http
-https://idp.wallet.example.com/interact/{id}/{nonce}
```
-
-
+https://auth.wallet.example.com/interact/{id}/{nonce}
+```
### Interaction endpoints
@@ -93,98 +84,64 @@ The endpoints are called in the sequence listed below.
-| Method | Endpoint | Purpose |
-| ---------------------------------------------------- | ------------------------------- | ----------------------------------------------------------------- |
-| | `/interact/{id}/{nonce}` | [Start user interaction session](#start-user-interaction-session) |
-| | `/grant/{id}/{nonce}` | [Look up grant information](#look-up-grant-information) |
-| | `/grant/{id}/{nonce}/{choice}` | [Accept or reject grant](#accept-or-reject-grant) |
-| | `/interact/{id}/{nonce}/finish` | [Finish user interaction](#finish-interaction)
|
-| | `/interact/{id}/{nonce}` | [Continue grant](#continue-grant) |
+| Method | Endpoint | Purpose | Called by | Publicly exposed |
+| ---------------------------------------------------- | ------------------------------- | ----------------------------------------------------------------- | -------------------- | ---------------- |
+| | `/interact/{id}/{nonce}` | [Start user interaction session](#start-user-interaction-session) | Open Payments client | Yes |
+| | `/grant/{id}/{nonce}` | [Look up grant information](#look-up-grant-information) | Identity provider | No |
+| | `/grant/{id}/{nonce}/{choice}` | [Accept or reject grant](#accept-or-reject-grant) | Identity provider | No |
+| | `/interact/{id}/{nonce}/finish` | [Finish user interaction](#finish-interaction) | Identity provider | Yes |
+| | `/interact/{id}/{nonce}` | [Continue grant](#continue-grant) | Open Payments client | Yes |
-We also provide an OpenAPI specification that describes the endpoints.
+We also provide an OpenAPI specification that describes the endpoints. Note that the _Continue grant_ endpoint is not included in the spec because it's part of the Open Payments Auth Server API.
#### Start user interaction session
-Called by the client to establish an interactive session with your authorization server. Also redirects the client's browser session to your IdP consent screen.
+Called by the client to establish an interactive session with the authorization server. The authorization server automatically redirects the request, via the URL defined in the `IDENTITY_SERVER_URL` variable, to your IdP consent screen.
#### Look up grant information
-Called by your IdP server, and secured with an [`x-idp-secret`](#x-idp-secret-header) header, to get the list of access rights the client requested from your authorization server.
-
-The access rights are presented to the client's end-user on the consent screen. The authorization server's response is served on your defined [`INTERACTION_PORT`](#environment-variables).
+Called by the IdP server to retrieve a list of access rights, requested by the client, from the authorization server. The request is secured with an [`x-idp-secret`](#x-idp-secret-header) header. The access rights are presented to the client's end-user on the consent screen. The authorization server's response is served on your configured `INTERACTION_PORT`.
#### Accept or reject grant
-Called by your IdP server, and secured with an [`x-idp-secret`](#x-idp-secret-header) header, to communicate the choice made by the end-user on the consent screen (accept/deny) to your authorization server.
-
-Your IdP server then redirects to the `finish` endpoint. The response is served on your defined [`INTERACTION_PORT`](#environment-variables)
+The IdP server communicates the choice made by the end-user on the consent screen (accept/reject) to the authorization server. The request is secured with an [`x-idp-secret`](#x-idp-secret-header) header. The authorization server responds to the IdP server, acknowledging that it received the request.
#### Finish interaction
-Called by your IdP server to end the interaction and redirect the end-user's browser session to the URI of the grant initialization request.
-
-The `result` query parameter will indicate the success or failure of the grant authorization. When successful, the SHA-256 hash of the interaction is sent in the response along with an `interact_ref` that identifies the interaction on your authorization server and the URI of the grant initialization request.
+Called by the IdP server to end the interaction. If a `finish` URI was provided in the original grant initialization request, the authorization server redirects the user to that URI.
-The following are examples of the possible response types.
+The `result` query parameter in the response indicates the success or failure of the grant authorization. The following are examples of the possible response types.
-| Response | Description | Example |
-| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
-| Rejected | The interaction was rejected by the end-user. | `?result=grant_rejected` |
-| Invalid | The grant was not in a state where it could be accepted or rejected (e.g., grant was already approved) | `?result=grant_invalid` |
-| Success | The grant was successful with the following returned in the response:
- A hash representing the SHA-256 hash of values provided by the client in the grant initialization request (`interact.finish.nonce`), and the values in the response returned from your authorization server (`interact.finish`).
- The `interact_ref` that identifies the interaction on your authorization server alongside the hash
- The URI of the grant initialization request (e.g., `https://www.auth-server.com`)
| `hash=p28jsq0Y2KK3WS__a42tavNC64ldGTBroywsWxT4md_jZQ1R\HZT8BOWYHcLmObM7XHPAdJzTZMtKBsaraJ64A &interact_ref=4IFWWIKYBC2PQ6U56NL1` |
+| Response | Description | Example |
+| -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
+| Rejected | The interaction was rejected by the end-user | `?result=grant_rejected` |
+| Invalid | The grant was not in a state where it could be accepted or rejected (e.g., grant was already approved) | `?result=grant_invalid` |
+| Success | The grant was successful with the following returned in the response:
- A hash representing the SHA-256 hash of values provided by the client in the grant initialization request (`interact.finish.nonce`), and the values in the response returned from the authorization server (`interact.finish`).
- The `interact_ref` that identifies the interaction on the authorization server alongside the hash
- The URI of the grant initialization request (e.g., `https://www.auth-server.com`)
| `hash=p28jsq0Y2KK3WS__a42tavNC64ldGTBroywsWxT4md_jZQ1R\HZT8BOWYHcLmObM7XHPAdJzTZMtKBsaraJ64A &interact_ref=4IFWWIKYBC2PQ6U56NL1` |
-#### Continue grant
+When successful, the SHA-256 hash of the interaction is sent in the response to the client, along with an `interact_ref` that identifies the interaction on the authorization server and the URI of the grant initialization request. The client must verify the hash before it will request to continue the grant.
-Called by the client to request a grant from your authorization server if the interaction was successful (accepted). Your authorization server responds with an access token.
+#### Continue grant
-### x-idp-secret header
+The client requests a grant from the authorization server for an accepted interaction. The authorization server responds with an access token.
-The purpose of the `x-idp-secret` header is to secure communications between your IdP and authorization servers.
+## x-idp-secret header
-The header is used for requests to the following endpoints and its value should be a shared secret known to both entities.
+The `x-idp-secret` header is specific to Rafiki's authorization server and is used for requests to the following endpoints:
- `GET /grant/:id/:nonce`
- `POST /grant/:id/:nonce/accept`
- `POST /grant/:id/:nonce/reject`
-When your IdP server sends requests to your authorization server, your IdP must provide the secret via this header.
-
-To set up the header, set the [`IDENTITY_SERVER_SECRET`](#environment-variables) on your authorization server environment to a value that is also used to configure your IdP server's requests to your authorization server.
-
-### Sequence diagram
-
-The following diagram provides a high-level view of the flow from when a pending grant request is created through to the authorization server returning an access token to continue a successful (accepted) interaction.
-
-The diagram is for illustrative purposes and is not an exact representation of the flow. Additional information can be found in the Open Payments documentation.
-
-
-
-{/* prettier-ignore */}
->Authorization Server: Sends interactive outgoing payment grant request
- Authorization Server-->>Client: HTTP 200 request successful
- Client->>Authorization Server: Starts user interaction session
- Authorization Server->>Authorization Server: Sets session
- Authorization Server-->>Client: HTTP 302 instructs client to redirect to Identity Provider
- Client->>Identity Provider: Redirects end-user's browser to the Identity Provider's consent screen
- Identity Provider->>Identity Provider: End-user accepts interaction
- Identity Provider->>Authorization Server: Provides end-user's interaction choice
- Authorization Server-->>Identity Provider: 202 HTTP choice accepted
- Identity Provider->>Authorization Server: Instructs server to finish interaction
- Authorization Server->>Authorization Server: Ends session
- Authorization Server-->>Identity Provider: 302 HTTP instructs Identity Provider to redirect to client
- Identity Provider->>Client: Redirects to Client
- Client->>Client: Verifies hash
- Client->>Authorization Server: Requests continuation of grant
- Authorization Server->>Client: 200 HTTP OK, returns access token
-`}
-/>
-
-
+The header's purpose is to secure communications between the IdP and the authorization server. Its value should be a shared secret known to both entities. When the IdP server sends requests to the authorization server, the IdP must provide the secret via this header.
+
+:::note
+If you're running your own authorization server rather than using the server provided by Rafiki, you can add security in any way you see fit. You aren't required to use the `x-idp-secret` header.
+:::
+
+To set up the header, set the `IDENTITY_SERVER_SECRET` variable to a value that is also used to configure your IdP server's requests to the authorization server.
diff --git a/packages/documentation/src/content/docs/integration/requirements/wallet-addresses.mdx b/packages/documentation/src/content/docs/integration/requirements/wallet-addresses.mdx
index fdc457bd23..4915fb07d0 100644
--- a/packages/documentation/src/content/docs/integration/requirements/wallet-addresses.mdx
+++ b/packages/documentation/src/content/docs/integration/requirements/wallet-addresses.mdx
@@ -8,9 +8,10 @@ import { CodeBlock } from '@interledger/docs-design-system'
Each payment account belonging to your users (e.g., customers) must have at least one associated wallet address for the account to be able to send and receive payments over Interledger and Open Payments. A wallet address serves as a publicly shareable standardized ID for a payment account.
:::note[Wallet address requirements]
+
- Your Rafiki instance must be set up for at least one asset before wallet addresses can be created as each wallet address must have an asset assigned to it.
- Wallet address URLs are treated as case-insensitive, meaning that both lowercase and uppercase variations of the same address will be recognized as identical.
-:::
+ :::
## Create wallet addresses
@@ -83,12 +84,12 @@ We strongly recommend you store at least the `walletAddress.id` in your internal
-|Variable | Description |
-|-------- | ----------- |
-| `assetId` | The unique ID of the asset, assigned by Rafiki when the asset was created, that the wallet address's underlying payment account is denominated in |
-| `publicName` | The public name associated with the wallet address |
-| `url` | The wallet address's case-insensitive URL |
-| `additionalProperties` | Optional [additional properties](/apis/graphql/backend/inputobjects/#additionalpropertyinput) associated with the wallet address |
+| Variable | Description |
+| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `assetId` | The unique ID of the asset, assigned by Rafiki when the asset was created, that the wallet address's underlying payment account is denominated in |
+| `publicName` | The public name associated with the wallet address |
+| `url` | The wallet address's case-insensitive URL |
+| `additionalProperties` | Optional [additional properties](/apis/graphql/backend/inputobjects/#additionalpropertyinput) associated with the wallet address |
@@ -193,11 +194,11 @@ Open Payments
-| Parameter | Required value | Description |
-| --------- | -------------- | ----------- |
-| `alg` | `EdDSA` | The algorithm used to generate the key pair |
-| `kty` | `OKP` | The key type identifying the cryptographic algorithm family used with the key |
-| `crv` | `Ed25519` | The cryptographic curve used with the key |
+| Parameter | Required value | Description |
+| --------- | -------------- | ----------------------------------------------------------------------------- |
+| `alg` | `EdDSA` | The algorithm used to generate the key pair |
+| `kty` | `OKP` | The key type identifying the cryptographic algorithm family used with the key |
+| `crv` | `Ed25519` | The cryptographic curve used with the key |
@@ -284,4 +285,4 @@ mutation RevokeWalletAddressKey($input: RevokeWalletAddressKeyInput!) {
}
```
-
\ No newline at end of file
+
diff --git a/packages/documentation/src/content/docs/integration/requirements/webhook-events.mdx b/packages/documentation/src/content/docs/integration/requirements/webhook-events.mdx
index b9e6328114..8fe55ea664 100644
--- a/packages/documentation/src/content/docs/integration/requirements/webhook-events.mdx
+++ b/packages/documentation/src/content/docs/integration/requirements/webhook-events.mdx
@@ -5,12 +5,7 @@ tableOfContents:
---
import { Badge, Tabs, TabItem, Steps } from '@astrojs/starlight/components'
-import {
- Mermaid,
- CodeBlock,
- LinkOut,
- Disclosure
-} from '@interledger/docs-design-system'
+import { Mermaid, CodeBlock, LinkOut } from '@interledger/docs-design-system'
The main communication channel between you and your Rafiki instance is composed of the Backend Admin API and a set of webhook events.
@@ -52,7 +47,8 @@ Each webhook event is sent as a JSON payload with the following structure in the
The `id` in the webhook event payload is unique. Your system can use the ID to determine whether the event has been received previously, preventing duplicate event processing.
:::
-
+
+Expand for example JSON payloads
```json
@@ -133,7 +129,7 @@ The `id` in the webhook event payload is unique. Your system can use the ID to d
```
-
+
We provide an OpenAPI specification for the webhook events fired by Rafiki.
@@ -176,7 +172,8 @@ The first retry is after 10 seconds. Additional retries occur after 20 more seco
#### Incoming payment created
-
+
+Expand for event sequence
-
+
The `incoming_payment.created` event indicates an incoming payment was created. At this point, the incoming payment has not received any funds.
@@ -196,8 +193,8 @@ The incoming payment will either complete or expire.
#### Incoming payment completed
-
-
+
+Expand for event sequences
{' '}
An incoming payment
@@ -234,13 +231,14 @@ of $10 was completed.
`}
/>
-
+
The `incoming_payment.completed` event indicates the payment completed either automatically or manually, and that any funds received into the incoming payment should be withdrawn and then credited to the recipient's account on your ledger.
#### Incoming payment expired
-
+
+Expand for event sequence
\$2.55 was received before the payment expired. The recipient is thus credited with \$2.55.
-
+
The `incoming_payment.expired` event will only fire if funds were received for the incoming payment. The event signals the end of any additional payments.
@@ -279,8 +277,8 @@ In some scenarios, a sender may not have specified an `incomingAmount` when the
#### Outgoing payment created
-
-
+
+Expand for event sequence
An outgoing payment for \$12 was created.
-
+
The `outgoing_payment.created` event indicates an outgoing payment was created and is awaiting liquidity. Verify the sender's account balance and perform any other necessary verifications before funding the payment.
@@ -308,8 +306,8 @@ If the sender has insufficient funds or if the payment should otherwise not be f
#### Outgoing payment completed
-
-
+
+Expand for event sequences
{' '}
An outgoing payment
@@ -343,7 +341,7 @@ for \$12 is complete. \$11.50 was sent. You choose to keep \$0.50 as a service f
`}
/>
-
+
The `outgoing.payment_completed` event indicates that as much as possible has been sent to the recipient against their incoming payment.
@@ -351,8 +349,8 @@ If there is excess liquidity in Rafiki due to differences between the sent and r
#### Outgoing payment failed
-
-
+
+Expand for event sequence
An outgoing payment for \$12 failed. \$8 was sent successfully.
-
+
The `outgoing_payment.failed` event indicates that an outgoing payment has either partially or completely failed and a retry was unsuccessful. Withdraw any remaining liquidity from the outgoing payment in Rafiki. If the payment failed completely (the `sentAmount` is `0`), remove the hold from your sender's account. If the payment partially failed, remove the hold from your sender's account, then debit the sender's account on your ledger with the amount that was sent successfully. Since there will be a discrepancy between the quoted amount and the actual sent amount, we suggest you refrain from taking a sending fee.
@@ -384,8 +382,8 @@ The `outgoing_payment.failed` event indicates that an outgoing payment has eithe
#### Wallet address not found
-
-
+
+Expand for event sequence
The wallet address, `https://wallet.example.com/carla_garcia` was requested but does not exist.
-
+
The `wallet_address.not_found` event indicates that a wallet address was requested via the Open Payments wallet address API call, but the address doesn’t exist in your Rafiki instance.
@@ -415,8 +413,8 @@ When you receive this event, look up the associated account in your system and c
#### Wallet address Web Monetization
-
-
+
+Expand for event sequence
A wallet address received a Web Monetization payment of \$0.33
-
+
The `wallet_address.web_monetization` event indicates that a wallet address received Web Monetization payments via the ILP STREAM protocol. Withdraw the liquidity from the wallet address in Rafiki and credit the recipient's account on your ledger.
@@ -447,8 +445,8 @@ The `wallet_address.web_monetization` event indicates that a wallet address rece
#### Asset liquidity low
-
-
+
+Expand for event sequence
Your asset liquidity for USD (asset scale: 2) drops below \$100.00.
-
+
The `asset.liquidity_low` event indicates that an asset's liquidity has dropped below your predefined liquidity threshold. Check if you already have, or can acquire, additional liquidity for that specific asset. If so, deposit it in Rafiki. Cross-currency transfers will fail if you don't increase the asset's liquidity.
@@ -478,8 +476,8 @@ The `asset.liquidity_low` event indicates that an asset's liquidity has dropped
#### Peer liquidity low
-
-
+
+Expand for event sequence
The liquidity for your peer, Happy Life Bank, drops below \$100.00 USD.
-
+
The `peer.liquidity_low` event indicates that a peer's liquidity has dropped below your predefined liquidity threshold. Decide whether you want to extend the peer's credit line or if your peer must settle before you will extend a new line of credit. If you cannot or do not increase the peer liquidity in Rafiki, transfers to that peer will fail.
diff --git a/packages/documentation/src/content/docs/overview/concepts/accounting.mdx b/packages/documentation/src/content/docs/overview/concepts/accounting.mdx
index 9ea2b75982..020062360e 100644
--- a/packages/documentation/src/content/docs/overview/concepts/accounting.mdx
+++ b/packages/documentation/src/content/docs/overview/concepts/accounting.mdx
@@ -1,139 +1,278 @@
---
-title: Accounting concepts
+title: Accounting in Rafiki
+tableOfContents:
+ maxHeadingLevel: 4
---
-import { LinkOut } from '@interledger/docs-design-system'
+import {
+ LinkOut,
+ CodeBlock,
+ Mermaid,
+ StylishHeader
+} from '@interledger/docs-design-system'
+import { Steps } from '@astrojs/starlight/components'
-import { CodeBlock } from '@interledger/docs-design-system'
+Rafiki uses double-entry accounting to record financial transactions. In this method of bookkeeping, a transaction recorded to one account results in an equal and opposite entry to another account. For example, a \$50 credit to one account will result in a \$50 debit from another account.
-## Accounts and assets
+Transactions in Rafiki represent Interledger packet interactions, denominated in a given [asset](#assets). Packet interactions can be successful, fail, or be rejected. Rafiki's accounting layer processes the interactions and converts the activities into financial records, which are then written to your [accounting database](#accounting-databases).
-Rafiki uses a combination of liquidity (credit) and settlement (debit) accounts to perform double-entry accounting.
+Accounts within Rafiki are your internal [liquidity](#liquidity-accounts) and [settlement](#settlement-accounts) accounts used to fund payments, not the accounts that you service for your customers. This distinction is crucial for understanding how Rafiki handles transactions and settlements. Some accounts are created on-the-fly during payment processing, while others must be created when integrating with Rafiki or setting up a peer.
-In this context, accounts in Rafiki are specifically the accounts that peers hold with one another. These are not customer accounts. This distinction is crucial for understanding how Rafiki handles transactions and settlements.
+## Assets
-### Liquidity accounts
+An asset represents a transferrable item of value. Although the Interledger Protocol (ILP) supports the transfer of any asset deemed to have value, assets are generally denominated in a currency. For example fiat currencies, central bank digital currencies, and branded currencies (such as merchant reward points).
-A liquidity account holds a non-negative balance, with Rafiki ensuring that total debits do not exceed total credits.
+Part of Rafiki's [integration requirements](/integration/requirements/assets) include adding one or more assets that you support.
-There is one liquidity account for each of the following resources:
+An asset is made up of the following properties.
-- Asset
-- Peer
-- Wallet address
-- Incoming payment
-- Outgoing payment
+
-### Settlement accounts
+| Property | Type | Description | Example |
+| ------------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
+| `value` | BigInt | A numerical amount | `10000` |
+| `assetCode` | String | A code representing the asset. An ISO 4217 currency code should be used whenever possible. | `"USD"` |
+| `assetScale` | Integer | Difference in order of magnitude between the standard unit and a fractional unit | `2` |
-A settlement account holds a non-positive balance, with Rafiki ensuring that total credits do not exceed total debits. A settlement account represents the total funds you as the ASE have deposited into Rafiki.
+
-Before you can peer with another ASE, you must both agree on the asset you will use for settlement. The Interledger packets exchanged between you and your peer will be denominated in the agreed-upon asset.
+To convert an asset’s value into an amount that’s easier to interpret, apply the following formula.
-There is one settlement account for each asset.
+$\frac{value}{10^{assetScale}}$ = _currencyAmount_
-### Assets
+Using the example data from the table above, the formula looks like this:
-An asset represents an item of value that can be transferred via the Interledger Protocol. Since the Interledger Protocol aims to create an internet of value, it allows for the transfer of any asset, not just currency. In practice, however, assets are usually denominated in a currency (fiat or branded currencies).
+$\frac{10000}{10^2} =\$100.00 USD
-In Rafiki, the `asset` type is comprised of the following properties.
+## Accounts
-
+Rafiki uses a combination of liquidity and settlement accounts to track the amounts available to fund transactions. Rafiki does not physically hold funds in each account. Instead, it uses double-entry accounting to record the transactions. The actual settlement of amounts owed, in which funds are physically exchanged, occurs outside of both Rafiki and the Interledger Protocol.
-| Property | Type | Description | Example |
-| ------------ | ------- | ---------------------------------------------------------------------------------------------------------------------- | ------- |
-| `value ` | BigInt | A numerical amount | `10000` |
-| `assetCode` | String | Should be an ISO 4217 currency code whenever possible | `"USD"` |
-| `assetScale` | Integer | Difference in order of magnitude between the standard unit and a fractional unit | `2` |
+### Liquidity accounts
-
+Liquidity accounts are used to track deposits, withdrawals, and transfers that occur during the course of a transaction. Liquidity accounts are provided for assets, peers, and payments.
-To convert an asset amount into a currency amount that's easier to read, apply the following formula:
+Liquidity accounts hold either a zero or a positive balance. Rafiki ensures that the total debits to a liquidity account will not exceed the account's total credits.
-$currencyAmount = \frac{value}{10^{assetScale}}$
+
-Using the example from the table above, our formula looks like this:
+| Account type | What the account represents | Number of accounts |
+| ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------- | ------------------------ |
+| [Asset liquidity](#asset-liquidity-accounts) | The value, denominated in a given asset, that Rafiki has available to support cross-currency transactions | One per asset |
+| [Peer liquidity](#peer-liquidity-accounts) | The credit line, denominated in the asset of your peering relationship, that you extend to a peer | One per peer |
+| [Incoming payment liquidity](#incoming-payment-liquidity-accounts) | The value received from a completed incoming payment | One per incoming payment |
+| [Outgoing payment liquidity](#outgoing-payment-liquidity-accounts) | The value that Rafiki will attempt to send in an outgoing payment | One per outgoing payment |
+| [Wallet address liquidity](#wallet-address-liquidity-accounts) | The value of an incoming payment received to a wallet address via either SPSP or Web Monetization | One per wallet address |
-$\frac{10000}{10^2} =100.00$ USD
+
-### TigerBeetle
+#### Asset liquidity accounts
-TigerBeetle is a high-performance distributed financial accounting database used by Rafiki’s `backend` service to store account balance data at the ILP layer. Both liquidity and settlement accounts in Rafiki correspond to TigerBeetle credit and debit accounts, respectively. TigerBeetle only holds balance data without any additional ILP packet metadata. For detailed information on TigerBeetle, including its consensus mechanism and its limitations, visit the official TigerBeetle documentation and blog.
+Asset liquidity ensures Rafiki has enough liquidity, denominated in a given asset, to handle cross-currency (foreign exchange) transactions.
-You have the flexibility to choose whether to use TigerBeetle or opt for a separate Postgres database. However, TigerBeetle is recommended due to its speed, efficiency, and dedicated design for handling double ledger accounting. For more information about Tigerbeetle in a production environment, see [Running Rafiki in production](/integration/prod/helm-k8s/#tigerbeetle).
+An asset liquidity account represents the value that Rafiki has available for sending or forwarding ILP packets. You have one asset liquidity account for each asset you transact in. The amount in an asset liquidity account increases when packets are received and decreases when packets are sent/forwarded. Any transaction that would result in a negative balance will fail.
-## Liquidity
+:::note
+If you and your peer transact in the same asset (there's no currency conversion) and you both provide your customers only with wallet addresses denominated in that asset, then there will be no movement into/from the corresponding asset liquidity account.
+
+For example, you and your peer transact in USD and only provide your customers with USD wallet addresses. One of your customers sends $10 to your peer's customer. There's no movement from your USD asset liquidity account because there was no currency conversion. There is, however, an [outgoing payment liquidity account](/overview/concepts/accounting#outgoing-payment-liquidity-accounts) created to support the transaction.
+:::
-Rafiki tracks liquidity using the Interledger Protocol, which is a clearing protocol, without physically holding funds.
+You can add a liquidity threshold for each asset liquidity account via the [`updateAsset`](https://rafiki.dev/apis/graphql/backend/mutations/#updateasset) mutation's `liquidityThreshold` input argument.
-### Asset liquidity
+When a threshold is entered, the [`asset.liquidity_low`](/integration/requirements/webhook-events#asset-liquidity-low) webhook event will notify you if an asset account's liquidity drops below the threshold.
-Asset liquidity represents the value, denominated in a given asset, that Rafiki has at its disposal in which to send or forward ILP packets. Asset liquidity increases when packets are received and decreases when packets are sent. Asset liquidity is always positive and cannot fall below zero.
+You should define and adjust asset liquidity based on your liquidity risk profile. You can deposit or withdraw asset liquidity as needed through [Rafiki Admin](/admin/admin-user-guide#edit-asset) or by using the [Backend Admin API](/admin/manage-liquidity#asset-liquidity).
-Additionally, you must provide asset liquidity if you are exchanging one currency for another. Asset liquidity ensures there is enough value available to handle transactions in the specified assets.
+
+Asset liquidity example - cross-currency transactions
+Your Rafiki instance is configured for two assets: EUR and USD.
-You should define and adjust asset liquidity based on your liquidity risk profile.
+- Rafiki holds an asset liquidity account for both EUR and USD.
+- You’ve set the asset scale of both currencies to 0.
+- Your starting EUR liquidity is 10 and your USD liquidity is 50.
-#### Asset liquidity examples
+**Cross-currency transaction #1:**
-Your Rafiki instance is configured with two assets: EUR and USD. You've set both to have an asset scale of 0. Your EUR liquidity is 10, and your USD liquidity is 50.
+
+ 1. Rafiki receives packets from a peer. These packets are all denominated in
+ EUR, worth €10. Your EUR liquidity increases to 20 (10 + 10). 2. The current
+ EUR-to-USD exchange rate is applied, with €10 equating to $12 USD. 3. Rafiki
+ moves $12 USD from your USD asset liquidity account into an incoming payment
+ liquidity account. Your USD liquidity decreases to 38 (50 - 12).
+
-In a cross currency transaction:
+**Cross-currency transaction #2:**
-- Rafiki receives packets worth 10 EUR and sends packets worth 11 USD. Your EUR liquidity increases to 20 (10 + 10) and your USD liquidity decreases to 39 (50 - 11).
-- Rafiki receives packets worth 50 EUR which equates to 55 USD. This transaction fails because Rafiki does not have enough USD liquidity.
+
+1. Rafiki receives packets from a peer. These packets are all denominated in EUR, worth €50. Your EUR liquidity increases to 70 (20 + 50).
+2. The current EUR-to-USD exchange rate is applied, with €50 equating to $55 USD.
+3. The transaction fails. Your USD liquidity account is 38, so you don't have enough liquidity to cover the transaction.
+4. Your EUR liquidity reduces back to 20 (70 - 50).
+
+
-### Peer liquidity
+#### Peer liquidity accounts
-Peer liquidity is the credit line, denominated in the asset of the peering relationship, you extend to a peer. Peer liquidity should be defined in the peering agreement and depends on the trust between you and your peer. If peer liquidity is insufficient, payments will not be processed. When peer liquidity is used up, you and your peer should settle then reset your liquidity.
+Peer liquidity is the credit line you've extended to a peer. A peer liquidity account represents the amount of the line of credit that the peer still has available to them. You have one liquidity account for each peer and the account is denominated in the asset you both agreed to transact in.
+
+The amount of credit that you extend to a peer, the asset that you transact in, and the mechanism you use to settle are just a few items that should be defined in your respective peering agreements.
:::note
-A peering agreement is a legal contract between the parties involved in a peering relationship. It defines terms such as the assets involved, auth tokens, connection endpoints, and other operational details. It is not configured or managed within Rafiki but is necessary for establishing the terms under which assets are exchanged between peers.
+A peering agreement is a legal contract between the parties involved in a peering relationship. It defines terms such as the assets involved, auth tokens, connection endpoints, and other operational details. It is not configured or managed within Rafiki but is necessary for establishing the terms under which assets are exchanged.
:::
-#### Peer liquidity example
+If a peer’s liquidity is insufficient (e.g., they’ve used up their allotted credit line), payments will not be processed. Your peer should settle with you so that you can reset their liquidity.
+
+You can add a liquidity threshold for each peer liquidity account via the [`updatePeer`](https://rafiki.dev/apis/graphql/backend/mutations/#updatepeer) mutation's `liquidityThreshold` input argument.
+
+When a threshold is entered, the [`peer.liquidity_low`](/integration/requirements/webhook-events#peer-liquidity-low) webhook event will notify you if a peer's liquidity drops below the threshold.
+
+You should define and adjust each peer's liquidity based on your liquidity risk profile. You can deposit or withdraw peer liquidity as needed through [Rafiki Admin](/admin/admin-user-guide#edit-peer) or by using the [Backend Admin API](/admin/manage-liquidity#peer-liquidity).
+
+
+Peer liquidity example
+You and Cloud Nine Wallet are peers. You’ve agreed to extend Cloud Nine Wallet a line of credit worth 100.00 USD. This means Cloud Nine Wallet has 100.00 in their peer liquidity account on your Rafiki instance.
+
+Your Rafiki instance can send packets that total up to 100.00 to wallet addresses issued by Cloud Nine Wallet. When the 100.00 is used up, Cloud Nine Wallet settles with you by sending 100.00 via the shared settlement mechanism outlined in your peering agreement. When you receive the funds, you reset their liquidity in Rafiki.
+
+
+
+#### Payment liquidity accounts
+
+Payment liquidity is the amount that's available because of an incoming or outgoing payment. Rafiki has three types of payment liquidity accounts.
+
+
+
+| Payment type | Purpose |
+| ---------------------------------------------------- | ---------------------------------------------------------- |
+| [Incoming](#incoming-payment-liquidity-accounts) | For incoming payments created via the Open Payments APIs |
+| [Outgoing](#outgoing-payment-liquidity-accounts) | For outgoing payments created via the Open Payments APIs |
+| [Wallet address](#wallet-address-liquidity-accounts) | For incoming payments created via SPSP or Web Monetization |
+
+
+
+##### Incoming payment liquidity accounts
+
+An incoming payment liquidity account represents the value received from a completed incoming payment. When an incoming payment is created via the Open Payments APIs, a corresponding liquidity account is automatically created. You will have one liquidity account per incoming payment.
+
+You are notified of created, completed, and expired incoming payments by listening for the appropriate [webhook events](/integration/requirements/webhook-events/#incoming-payments). Since Rafiki doesn't hold funds, anything you receive must be withdrawn and then credited to the recipient's account on your ledger.
+
+The liquidity account isn’t used again after the payment completes, but its record remains in your accounting database. When a new incoming payment occurs, a new liquidity account is created.
+
+##### Outgoing payment liquidity accounts
+
+An outgoing payment liquidity account represents the value available to send in an outgoing payment. When an outgoing payment is created via the Open Payments APIs, a corresponding liquidity account is automatically created. You will have one liquidity account per outgoing payment.
+
+You are notified of created, completed, and failed outgoing payments by listening for the appropriate [webhook events](/integration/requirements/webhook-events/#outgoing-payments). Liquidity must be deposited into the outgoing payment account before the payment can be processed.
+
+Since Rafiki doesn’t hold funds, any excess liquidity that remains after the outgoing payment completes must be withdrawn. While we recommend refunding the excess to the sender, the choice is ultimately up to you.
+
+The account isn’t used again after the payment completes, but its record remains in your accounting database. When a new outgoing payment is created, a new liquidity account is created.
+
+##### Wallet address liquidity accounts
+
+A wallet address liquidity account contains the value received to a wallet address via either SPSP or Web Monetization. When an incoming payment is created by one of these two methods, a corresponding liquidity account is automatically created. You will have one account per wallet address.
+
+Since Rafiki doesn’t hold funds, you must withdraw the liquidity when the payment completes and credit the funds to the recipient’s account on your ledger. You are notified to withdraw liquidity by listening for the appropriate [webhook event](/integration/requirements/webhook-events#wallet-addresses).
+
+Unlike the incoming and outgoing payment liquidity accounts, the same wallet address liquidity account is used for future incoming SPSP or Web Monetization payments.
+
+### Settlement accounts
+
+A settlement account represents the total funds, denominated in a specified asset, that you have deposited into Rafiki. You have one settlement account for each asset you transact in.
+
+Settlement accounts hold either a zero or a negative balance. A negative balance on a settlement account means you've deposited more funds into Rafiki than you've taken out.
+
+Rafiki ensures that the total credits to a settlement account will not exceed its total debits.
+
+
+Settlement account example
+You deposit \$10,000 into a peer's liquidity account, meaning you've extended a credit line of \$10,000 to your peer.
+
+Your peer liquidity account balance is \$10,000 and your USD settlement account balance is now -\$10,000.
+
+An incoming payment from your peer for \$100 is created, meaning your peer is using \$100 of their line of credit. Since Rafiki doesn't hold funds, you must withdraw the liquidity and credit the amount to the recipient's account on your ledger.
+
+Now, your peer liquidity account's balance is \$9,900 and your USD settlement account's balance is -\$9,900.
+
+
+
+A negative balance on a settlement account means you've deposited more funds into Rafiki than you've withdrawn. The closer a settlement account's balance is to 0, the more likely it is you need to settle with your peer for the amount owed and then deposit the amount back into Rafiki.
+
+## Accounting databases
+
+### TigerBeetle
-Your peer, Cloud Nine Wallet, has a peer liquidity of 100 USD. Rafiki can send packets that total up to 100 USD to wallet addresses issued by Cloud Nine Wallet.
+TigerBeetle is a high-performance distributed financial accounting database used by Rafiki’s backend service to store account balance data at the ILP layer. Both liquidity and settlement accounts in Rafiki correspond to TigerBeetle credit and debit accounts, respectively.
-After the 100 USD liquidity is used up, you settle with Cloud Nine Wallet and reset the peer liquidity in Rafiki to 100 USD.
+TigerBeetle only holds balance data without any additional ILP packet metadata. For detailed information on TigerBeetle, including its consensus mechanism and its limitations, visit the official TigerBeetle documentation and blog. For more information about TigerBeetle in a production Rafiki environment, see [Running Rafiki in production](/integration/prod/helm-k8s/#running-rafiki-in-production).
-### Payment liquidity
+### Postgres
-Payment liquidity is managed for incoming and outgoing payments created via Open Payments through liquidity accounts in your accounting database. When incoming or outgoing payments are created via the Open Payments APIs, a corresponding liquidity account is automatically created. Liquidity must be deposited to an outgoing payment account before the payment can be processed. You are notified to deposit or withdraw liquidity via [webhook events](/integration/requirements/webhook-events).
+You can choose to use a separate Postgres database for accounting instead of using TigerBeetle. However, TigerBeetle is recommended due to its speed, efficiency, and dedicated design for handling double-entry/double-ledger accounting.
## Transfers
-Transfers in Rafiki are based on double-entry accounting, increasing both the total debits (withdrawals )of one account and the total credits (deposits) of another by the same amount.
+As with the accounts described above, Rafiki performs double-entry accounting for transfers, where increasing the total debits of one account increases the total credits of another account by the same amount, and vice versa.
+
+Transfers can be completed in either a single phase or in two phases.
### Single-phase transfer
A single-phase transfer posts funds to accounts immediately when the transfer is created.
+**Example of successful single-phase incoming payment**
+
+>ASE: Fires webhook event when incoming payment completes
+ ASE->>R: Withdraws payment amount from incoming payment liquidity account
+ ASE->>ASE: Credits the recipient's account by the payment amount
+
+`}
+/>
+
### Two-phase transfer
-A two-phase transfer moves funds in stages.
+A two-phase transfer moves funds in two stages.
1. Reserve funds (`pending`)
2. Resolve funds (`post`, `void`, or `expire`)
-The name _two-phase transfer_ is a reference to the two-phase commit protocol for distributed transactions.
+**Example of successful two-phase incoming payment**
+
+>ASE: Fires webhook event when incoming payment completes
+ ASE->>Rafiki: Withdraws payment amount from incoming payment liquidity account (reserve funds pending)
+ ASE->>ASE: Credits the recipient's account by the payment amount
+ ASE->>Rafiki: Resolve funds (post)
+ Rafiki->>Rafiki: Two-phase transfer complete
+ `}
+/>
+
+The name two-phase transfer is a reference to the two-phase commit protocol for distributed transactions.
-### Intra-Rafiki transfer examples
+### Transfer examples
+
+#### Intra-Rafiki transfer examples
Remember that a settlement account will always have a zero or negative balance and a liquidity account will always have a zero or positive balance.
-#### Deposits
+Deposits
A deposit is the act of debiting the settlement account and crediting the liquidity account.
-**Depositing asset liquidity**
+**Example:** Depositing `100 USD` asset liquidity
| Debit Account | Credit Account |
| ------------- | --------------- |
| Settlement | Asset liquidity |
-**Example:** Depositing `100 USD`
-
-**Depositing peer liquidity**
+
+
+**Example:** Depositing `100 USD` peer liquidity
| Debit Account | Credit Account |
| ------------- | -------------- |
| Settlement | Peer liquidity |
-**Example:** Peering relationship in USD, depositing `100 USD`
-
-**Depositing outgoing payment liquidity**
+
+
+**Example:** Depositing `35 USD` outgoing payment liquidity
| Debit Account | Credit Account |
| ------------- | ---------------- |
| Settlement | Outgoing payment |
-**Example:** Depositing `35 USD`
-
-#### Withdrawals
+
+
+Withdrawals
A withdrawal is the act of debiting the liquidity account and crediting the settlement account.
-**Withdrawing asset liquidity**
+**Example:** Withdrawing `50 USD` in asset liquidity
| Debit Account | Credit Account |
| --------------- | -------------- |
| Asset liquidity | Settlement |
-**Example:** Withdrawing `50 USD`
-
-**Withdrawing peer liquidity**
+
+
+**Example:** Withdrawing `50 USD` in peer liquidity
| Debit Account | Credit Account |
| -------------- | -------------- |
| Peer liquidity | Settlement |
-**Example:** Peering relationship in USD, withdrawing `50 USD`
-
-**Withdrawing wallet address liquidity**
+
+
+**Example:** Withdrawing `2 USD` in wallet address liquidity
| Debit Account | Credit Account |
| -------------- | -------------- |
| Wallet address | Settlement |
-**Example:** Withdrawing `2 USD`
-
-**Withdrawing incoming payment liquidity**
+
+
+**Example:** Withdrawing `2 USD` in incoming payment liquidity
| Debit Account | Credit Account |
| ---------------- | -------------- |
| Incoming payment | Settlement |
-**Example:** Withdrawing `25 USD`
-
-**Withdrawing outgoing payment liquidity**
+
+
+**Example:** Withdrawing `1 USD` in outgoing payment liquidity
| Debit Account | Credit Account |
| ---------------- | -------------- |
| Outgoing payment | Settlement |
-**Example:** Withdrawing `1 USD`
-
-#### Payments in the same asset
+
+
+Payments in the same asset
-**Send amount < receive amount**
+**Example:** Sender consented to a payment of `14 USD` but the quote promised to deliver `15 USD`. The send amount is less than the receive amount.
| Debit Account | Credit Account |
| ---------------- | ---------------- |
| Outgoing payment | Incoming payment |
| Asset liquidity | Incoming payment |
-**Example:** Sender consented to a payment of `14 USD` but the quote promised to deliver `15 USD`.
-
-**Send amount > receive amount**
+
+
+**Example:** Sender consented to a payment of `15 USD` but the quote promised to deliver `14 USD`. The send amount is more than the receive amount.
| Debit Account | Credit Account |
| ---------------- | ---------------- |
| Outgoing payment | Incoming payment |
| Outgoing payment | Asset liquidity |
-**Example:** Sender consented to a payment of `15 USD` but the quote promised to deliver `14 USD`.
-
-#### Cross currency payments
+
-**Exchanging currencies**
+Cross currency payments
+
+**Example:** Outgoing payment is for `10 USD`, incoming payment receives `9 EUR` after a currency exchange
| Debit Account | Credit Account | Asset |
| ---------------- | ---------------- | ----- |
| Outgoing payment | Asset liquidity | `USD` |
| Asset liquidity | Incoming payment | `EUR` |
-**Example:** Outgoing payment for `10 USD`, incoming payment receives `9 EUR`.
-
-### Interledger transfer examples
+
+
+#### Interledger transfer examples
In these examples, the sender and receiver do not have wallet addresses at the same Rafiki instance.
Remember that a settlement account will always have a zero or negative balance and a liquidity account will always have a zero or positive balance.
-#### Sending connector
+Sending connector - same asset
-**Same asset**
+**Example:** Sender creates an outgoing payment for `100 USD` to an incoming payment in the same asset at a peer's Rafiki instance
| Debit Account | Credit Account |
| ---------------- | -------------- |
| Outgoing payment | Peer liquidity |
-**Example:** Sender creates an outgoing payment for 100 USD to an incoming payment at a peer's Rafiki instance. The peering relationship is in USD.
-
-**Cross currency**
+
+
+Sending connector - cross currency
+
+**Example:** Sender creates an outgoing payment for `100 USD` to an incoming payment at a peer's Rafiki instance. The peering relationship is in EUR, so the payment is converted on the sending side.
| Debit Account | Credit Account | Asset |
| ---------------- | --------------- | ----- |
| Outgoing payment | Asset liquidity | `USD` |
| Asset Liquidity | Peer Liquidity | `EUR` |
-**Example:** Sender creates an outgoing payment for 100 USD to an incoming payment at a peer's Rafiki instance. The peering relationship is in EUR, so payment is converted on the sending side.
-
-#### Receiving connector
+
+
+Receiving connector - same asset
-**Same asset**
+**Example:** An incoming payment receives `100 USD` from an outgoing payment in the same asset at a peer's Rafiki instance.
| Debit Account | Credit Account |
| -------------- | ---------------- |
| Peer liquidity | Incoming payment |
-**Example:** An incoming payment receives `100 USD` from an outgoing payment at a peer's Rafiki instance.
-
-**Cross currency**
+
+
+Receiving connector - cross currency
+
+**Example:** A Rafiki instance receives `10 USD` from a peer (peering relationship in USD) to be deposited in an incoming payment liquidity account denominated in EUR. The payment is converted to EUR and deposited.
| Debit Account | Credit Account | Asset |
| --------------- | ---------------- | ----- |
| Peer liquidity | Asset liquidity | `USD` |
| Asset liquidity | Incoming payment | `EUR` |
-**Example:** A Rafiki instance receives `10 USD` from a peer (peering relationship in USD) to be deposited in an incoming payment liquidity account denominated in EUR. The payment is converted to EUR and deposited.
-
-#### Connector
+
-**Same asset**
+Connector - same asset
+
+**Example:** Rafiki forwards `10 USD` from peer A to peer B.
| Debit Account | Credit Account |
| -------------- | -------------- |
| Peer liquidity | Peer liquidity |
-**Example:** Rafiki forwards `10 USD` from peer A to peer B.
-
-**Cross currency**
+
+
+Connector - cross currency
+
+**Example:** Rafiki receives `100 USD` from peer A and forwards `90 EUR` to peer B.
| Debit Account | Credit Account | Asset |
| --------------- | --------------- | ----- |
| Peer liquidity | Asset liquidity | `USD` |
| Asset liquidity | Peer liquidity | `EUR` |
-**Example:** Rafiki receives `100 USD` from peer A and forwards `90 EUR` to peer B.
-