Skip to main content

Getting Started

❗ Rafiki is intended to be run by Account Servicing Entities only and should not be used in production by non-regulated entities.

Account Servicing Entities provide and maintain payment accounts. In order to make these accounts Interledger-enabled via Rafiki, they need to provide the following endpoints and services:

Furthermore, each payment account managed by the Account Servicing Entity needs to be issued at least one payment pointer in order to be serviced by Rafiki and send or receive Interledger payments.

Quotes / Rates and Fees

Every Interledger payment is preceded with a quote that estimates the costs for transfering value from A to B. The Account Servicing Entity may charge fees on top of that for facilitating that transfer. How they structure those fees is completely up to the Account Servicing Entity.

Exchange Rates

For the quoting to be successful, Rafiki needs to be provided with the current exchange rate by the Account Servicing Entity. The Account Servicing Entity needs to expose an endpoint that accepts a GET requests and responds as follows.

Response Body

Variable NameTypeDescription
baseStringasset code represented as ISO 4217 currency code, e.g. USD
ratesObjectObject containing <asset_code : exchange_rate> pairs, e.g. {EUR: 1.1602}
rates.<asset_code>Numberexchange rate given base and <asset_code>

The response status code for a successful request is a 200. The mock-account-servicing-entity includes a minimalistic example.

The backend package requires an environment variable called EXCHANGE_RATES_URL which MUST specify the URL of this endpoint.

Fees

If the Account Servicing Entity decides to add sending fees, it is required to provide an endpoint that is accessible to the Rafiki backend. It accepts a POST request with

Request Body

Variable NameTypeDescription
idStringInterledger quote id
paymentTypeEnum: 'FixedSend' | 'FixedDelivery'fixed-send or fixed-receive payment
paymentPointerIdStringid of sending payment pointer
receiverStringreceiving payment pointer
sendAmountAmountdefined or quoted send amount
receiveAmountAmountdefined or quoted receive amount
createdAtStringcreation date and time of Interledger quote
expiresAtStringexpiry date and time of Interledger quote

Amount

(The example amount is $42.42.)

Variable NameTypeDescription
valueString // Number // biginte.g. "4242" or 4242
assetCodeStringISO 4217 currency code, e.g. USD
assetScaleNumberdifference in orders of magnitude between the standard unit and a corresponding fractional unit, e.g. 2

If the payment is a FixedSend payment, this endpoint should deduct its fees from the receive amount value. If the payment is a FixedDelivery payment, this endpoint should add the fees to the send amount value. The response body MUST be equal to the request body apart from the updated sendAmount or receiveAmount values. The response status code for a successful request is a 201. The mock-account-servicing-entity includes a minimalistic example.

The backend package requires an environment variable called QUOTE_URL which MUST specify the URL of this endpoint.

Webhook Events Listener

Rafiki itself does not hold any balances but needs to be funded for outgoing transfers and money needs to be withdrawn for incoming transfers. In order to notify the Account Servicing Entity about those transfer events, they need to expose a webhook endpoint that listens for these events and reacts accordingly.

The endpoint accepts a POST request with

Request Body

Variable NameTypeDescription
idStringevent id
typeEnum: EventType
dataObjectany additional data

EventType

ValueDescription
incoming_payment.createdIncoming payment has been created.
incoming_payment.completedIncoming payment is complete and doesn't accept any incoming funds anymore.
incoming_payment.expiredIncoming payment is expired and doesn't accept any incoming funds anymore.
outgoing_payment.createdOutgoing payment was created.
outgoing_payment.completedOutgoing payment is complete.
outgoing_payment.failedOutgoing payment failed.
payment_pointer.web_monetizationWeb Monetization payments received via STREAM.

The backend package requires an environment variable called WEBHOOK_URL which MUST specify this endpoint.

Event Handlers

incoming_payment.created

An Open Payments Incoming Payment was created. This is for information purposes only and does not require an action.

  • Action: None

incoming_payment.completed

An Open Payments Incoming Payment was completed, either manually or programmatically, i.e. it does not accept any incoming funds anymore. The Account Servicing Entity SHOULD withdraw all funds received and deposit them into the payee's account.

  • Action: Withdraw liquidity

incoming_payment.expired

An Open Payments Incoming Payment has expired, i.e. it does not accept any incoming funds anymore. The Account Servicing Entity SHOULD withdraw any funds already received and deposit them into the payee's account.

  • Action: Withdraw liquidity

outgoing_payment.created

An Open Payments Outgoing Payment has been created. It requires liquidity to be processed. The Account Servicing Entity SHOULD reserve the maximum requisite funds for the payment attempt on the payer's account.

  • Action: Deposit liquidity

outgoing_payment.completed

An Open Payments Outgoing Payment was completed, i.e. it won't send any further funds. The Account Servicing Entity SHOULD withdraw any excess liquidity and deposit it into the payer's account.

  • Action: Withdraw liquidity

outgoing_payment.failed

An Open Payments Outgoing Payment failed to send all (or any) of the funds and won't re-try. The Account Servicing Entity SHOULD withdraw all or any excess liquidity and return it to the payer's account.

  • Action: Withdraw liquidity

payment_pointer.web_monetization

A Web Monetization payment has been received via STREAM by a payment pointer. The Account Servicing Entity SHOULD withdraw all funds received and deposit them into the payee's account.

  • Action: Withdraw liquidity

Identity Provider

The Rafiki backend exposes the Open Payments APIs. They are auth-protected using an opinionated version of the Grant Negotiation Authorization Protocol (GNAP). Rafiki comes with a reference implementation of an Open Payments Auth Server--the auth package.

The Open Payments Auth Server requires integration with an Identity Provider to handle user authentication and consent. For more information on how to integrate an Identity Provider with the reference implementation of the Open Payments Auth Server, see the docs in the auth package.

Issuing Payment Pointers

A Payment Pointer is a standardized identifier for a payment account. It can be created using the Admin API. Note that at least one asset has to be created prior to creating the payment pointer since an assetId MUST be provided as input variable on payment pointer creation.

Create Asset

Query:

mutation CreateAsset ($input: CreateAssetInput!) {
createAsset(input: $input) {
code
success
message
asset {
id
code
scale
}
}
}

Query Variables:

{
"input": {
"code": "USD",
"scale": 2
}
}

Example Successful Response

{
"data": {
"createAsset": {
"code": "200",
"success": true,
"message": "Created Asset",
"asset": {
"id": "0ddc0b7d-1822-4213-948e-915dda58850b",
"code": "USD",
"scale": 2
}
}
}
}

Create Payment Pointer

Query:

mutation CreatePaymentPointer($input: CreatePaymentPointerInput!) {
createPaymentPointer(input: $input) {
code
success
message
paymentPointer {
id
createdAt
publicName
url
asset {
code
id
scale
}
}
}
}

Query Variables:

{
"input": {
"assetId": "0ddc0b7d-1822-4213-948e-915dda58850b",
"publicName": "Sarah Marshall",
"url": "https://example.wallet.com/sarah"
}
}

Example Successful Response

{
"data": {
"createPaymentPointer": {
"code": "200",
"success": true,
"message": "Created payment pointer",
"paymentPointer": {
"id": "695e7546-1803-4b45-96b6-6a53f4082018",
"createdAt": "2023-03-03T09:07:01.107Z",
"publicName": "Sarah Marshall",
"url": "https://example.wallet.com/sarah",
"asset": {
"id": "0ddc0b7d-1822-4213-948e-915dda58850b",
"code": "USD",
"scale": 2
}
}
}
}
}

The Account Servicing Entity SHOULD store at least the paymentPointer.id in their internal database to be able to reference the account and payment pointer.

Create Payment Pointer Key

In order to use the Open Payments APIs, a payment pointer needs to be associated with at least one private-public-keypair to be able to sign API request. One or multiple public keys are linked to the payment pointer such that third-parties can verify said request signatures. It can be added using the Admin API.

Query:

mutation CreatePaymentPointerKey($input: CreatePaymentPointerKeyInput!) {
createPaymentPointerKey(input: $input) {
code
message
success
paymentPointerKey {
id
paymentPointerId
revoked
jwk {
alg
crv
kid
kty
x
}
createdAt
}
}
}

Query Variables:

{
"input": {
"jwk": {
"kid": "keyid-97a3a431-8ee1-48fc-ac85-70e2f5eba8e5",
"x": "ubqoInifJ5sssIPPnQR1gVPfmoZnJtPhTkyMXNoJF_8",
"alg": "EdDSA",
"kty": "OKP",
"crv": "Ed25519"
},
"paymentPointerId": "695e7546-1803-4b45-96b6-6a53f4082018"
}
}

Example Successful Response

{
"data": {
"createPaymentPointerKey": {
"code": "200",
"message": "Added Key To Payment Pointer",
"success": true,
"paymentPointerKey": {
"id": "f2953571-f10c-44eb-ab41-4450a7ad6771",
"paymentPointerId": "695e7546-1803-4b45-96b6-6a53f4082018",
"revoked": false,
"jwk": {
"alg": "EdDSA",
"crv": "Ed25519",
"kid": "keyid-97a3a431-8ee1-48fc-ac85-70e2f5eba8e5",
"kty": "OKP",
"x": "ubqoInifJ5sssIPPnQR1gVPfmoZnJtPhTkyMXNoJF_8"
},
"createdAt": "2023-03-03T09:26:41.424Z"
}
}
}
}