diff --git a/packages/auth/src/graphql/generated/graphql.schema.json b/packages/auth/src/graphql/generated/graphql.schema.json index 420f2498dc..974e62f145 100644 --- a/packages/auth/src/graphql/generated/graphql.schema.json +++ b/packages/auth/src/graphql/generated/graphql.schema.json @@ -15,7 +15,7 @@ "fields": [ { "name": "actions", - "description": "Access action (create, read, list or complete)", + "description": "Actions allowed with this access.", "args": [], "type": { "kind": "NON_NULL", @@ -35,7 +35,7 @@ }, { "name": "createdAt", - "description": "Date-time of creation", + "description": "The date and time when the access was created.", "args": [], "type": { "kind": "NON_NULL", @@ -51,7 +51,7 @@ }, { "name": "id", - "description": "Access id", + "description": "Unique identifier of the access object.", "args": [], "type": { "kind": "NON_NULL", @@ -67,7 +67,7 @@ }, { "name": "identifier", - "description": "Wallet address of a sub-resource (incoming payment, outgoing payment, or quote)", + "description": "Wallet address of the sub-resource (incoming payment, outgoing payment, or quote).", "args": [], "type": { "kind": "SCALAR", @@ -79,7 +79,7 @@ }, { "name": "limits", - "description": "Payment limits", + "description": "Limits for an outgoing payment associated with this access.", "args": [], "type": { "kind": "OBJECT", @@ -91,7 +91,7 @@ }, { "name": "type", - "description": "Access type (incoming payment, outgoing payment, or quote)", + "description": "Type of access (incoming payment, outgoing payment, or quote).", "args": [], "type": { "kind": "NON_NULL", @@ -135,7 +135,7 @@ "inputFields": [ { "name": "in", - "description": null, + "description": "List of finalization reasons to include in the filter.", "type": { "kind": "LIST", "name": null, @@ -155,7 +155,7 @@ }, { "name": "notIn", - "description": null, + "description": "List of finalization reasons to exclude in the filter.", "type": { "kind": "LIST", "name": null, @@ -186,7 +186,7 @@ "inputFields": [ { "name": "in", - "description": null, + "description": "List of states to include in the filter.", "type": { "kind": "LIST", "name": null, @@ -206,7 +206,7 @@ }, { "name": "notIn", - "description": null, + "description": "List of states to exclude in the filter.", "type": { "kind": "LIST", "name": null, @@ -237,7 +237,7 @@ "inputFields": [ { "name": "in", - "description": null, + "description": "Array of strings to filter by.", "type": { "kind": "LIST", "name": null, @@ -267,7 +267,7 @@ "fields": [ { "name": "access", - "description": "Access details", + "description": "Details of the access provided by the grant.", "args": [], "type": { "kind": "NON_NULL", @@ -291,7 +291,7 @@ }, { "name": "client", - "description": "Wallet address of the grantee's account", + "description": "Wallet address of the grantee's account.", "args": [], "type": { "kind": "NON_NULL", @@ -307,7 +307,7 @@ }, { "name": "createdAt", - "description": "Date-time of creation", + "description": "The date and time when the grant was created.", "args": [], "type": { "kind": "NON_NULL", @@ -323,7 +323,7 @@ }, { "name": "finalizationReason", - "description": "Reason a grant was finalized", + "description": "Specific outcome of a finalized grant, indicating whether the grant was issued, revoked, or rejected.", "args": [], "type": { "kind": "ENUM", @@ -335,7 +335,7 @@ }, { "name": "id", - "description": "Grant id", + "description": "Unique identifier of the grant.", "args": [], "type": { "kind": "NON_NULL", @@ -351,7 +351,7 @@ }, { "name": "state", - "description": "State of the grant", + "description": "Current state of the grant.", "args": [], "type": { "kind": "NON_NULL", @@ -384,7 +384,7 @@ "fields": [ { "name": "cursor", - "description": null, + "description": "A cursor for paginating through the grants.", "args": [], "type": { "kind": "NON_NULL", @@ -400,7 +400,7 @@ }, { "name": "node", - "description": null, + "description": "A grant node in the list.", "args": [], "type": { "kind": "NON_NULL", @@ -428,7 +428,7 @@ "inputFields": [ { "name": "finalizationReason", - "description": null, + "description": "Filter grants by their finalization reason.", "type": { "kind": "INPUT_OBJECT", "name": "FilterFinalizationReason", @@ -440,7 +440,7 @@ }, { "name": "identifier", - "description": null, + "description": "Filter grants by their unique identifier.", "type": { "kind": "INPUT_OBJECT", "name": "FilterString", @@ -452,7 +452,7 @@ }, { "name": "state", - "description": null, + "description": "Filter grants by their state.", "type": { "kind": "INPUT_OBJECT", "name": "FilterGrantState", @@ -477,19 +477,19 @@ "enumValues": [ { "name": "ISSUED", - "description": "grant was issued", + "description": "The grant was issued successfully.", "isDeprecated": false, "deprecationReason": null }, { "name": "REJECTED", - "description": "grant was rejected", + "description": "The grant request was rejected.", "isDeprecated": false, "deprecationReason": null }, { "name": "REVOKED", - "description": "grant was revoked", + "description": "The grant was revoked.", "isDeprecated": false, "deprecationReason": null } @@ -506,25 +506,25 @@ "enumValues": [ { "name": "APPROVED", - "description": "grant was approved", + "description": "The grant request has been approved.", "isDeprecated": false, "deprecationReason": null }, { "name": "FINALIZED", - "description": "grant was finalized and no more access tokens or interactions can be made on it", + "description": "The grant request has been finalized, and no more access tokens or interactions can be made.", "isDeprecated": false, "deprecationReason": null }, { "name": "PENDING", - "description": "grant request is awaiting interaction", + "description": "The grant request is awaiting interaction.", "isDeprecated": false, "deprecationReason": null }, { "name": "PROCESSING", - "description": "grant request is determining what state to enter next", + "description": "The grant request is processing.", "isDeprecated": false, "deprecationReason": null } @@ -538,7 +538,7 @@ "fields": [ { "name": "edges", - "description": null, + "description": "A list of edges representing grants and cursors for pagination.", "args": [], "type": { "kind": "NON_NULL", @@ -562,7 +562,7 @@ }, { "name": "pageInfo", - "description": null, + "description": "Information to aid in pagination.", "args": [], "type": { "kind": "NON_NULL", @@ -609,7 +609,7 @@ "fields": [ { "name": "debitAmount", - "description": "Amount to debit", + "description": "Amount to debit.", "args": [], "type": { "kind": "OBJECT", @@ -621,7 +621,7 @@ }, { "name": "interval", - "description": "Interval between payments", + "description": "Interval between payments.", "args": [], "type": { "kind": "SCALAR", @@ -633,7 +633,7 @@ }, { "name": "receiveAmount", - "description": "Amount to receive", + "description": "Amount to receive.", "args": [], "type": { "kind": "OBJECT", @@ -645,7 +645,7 @@ }, { "name": "receiver", - "description": "Wallet address URL of the receiver", + "description": "Wallet address URL of the receiver.", "args": [], "type": { "kind": "SCALAR", @@ -668,7 +668,7 @@ "fields": [ { "name": "createdAt", - "description": null, + "description": "The date and time when the model was created.", "args": [], "type": { "kind": "NON_NULL", @@ -684,7 +684,7 @@ }, { "name": "id", - "description": null, + "description": "Unique identifier for the model.", "args": [], "type": { "kind": "NON_NULL", @@ -722,7 +722,7 @@ "fields": [ { "name": "revokeGrant", - "description": "Revoke Grant", + "description": "Revoke an existing grant.", "args": [ { "name": "input", @@ -766,7 +766,7 @@ "fields": [ { "name": "endCursor", - "description": "Paginating forwards: the cursor to continue.", + "description": "The cursor used to fetch the next page when paginating forward.", "args": [], "type": { "kind": "SCALAR", @@ -778,7 +778,7 @@ }, { "name": "hasNextPage", - "description": "Paginating forwards: Are there more pages?", + "description": "Indicates if there are more pages when paginating forward.", "args": [], "type": { "kind": "NON_NULL", @@ -794,7 +794,7 @@ }, { "name": "hasPreviousPage", - "description": "Paginating backwards: Are there more pages?", + "description": "Indicates if there are more pages when paginating backward.", "args": [], "type": { "kind": "NON_NULL", @@ -810,7 +810,7 @@ }, { "name": "startCursor", - "description": "Paginating backwards: the cursor to continue.", + "description": "The cursor used to fetch the next page when paginating backward.", "args": [], "type": { "kind": "SCALAR", @@ -833,7 +833,7 @@ "fields": [ { "name": "assetCode", - "description": "[ISO 4217 currency code](https://en.wikipedia.org/wiki/ISO_4217), e.g. `USD`", + "description": "Should be an ISO 4217 currency code whenever possible, e.g. `USD`. For more information, refer to [assets](https://rafiki.dev/overview/concepts/accounting/#assets).", "args": [], "type": { "kind": "NON_NULL", @@ -849,7 +849,7 @@ }, { "name": "assetScale", - "description": "Difference in orders of magnitude between the standard unit of an asset and a corresponding fractional unit", + "description": "Difference in orders of magnitude between the standard unit of an asset and a corresponding fractional unit.", "args": [], "type": { "kind": "NON_NULL", @@ -865,7 +865,7 @@ }, { "name": "value", - "description": null, + "description": "The value of the payment amount.", "args": [], "type": { "kind": "NON_NULL", @@ -892,11 +892,11 @@ "fields": [ { "name": "grant", - "description": "Fetch a grant", + "description": "Fetch a specific grant by its ID.", "args": [ { "name": "id", - "description": null, + "description": "Unique identifier of the grant.", "type": { "kind": "NON_NULL", "name": null, @@ -925,11 +925,11 @@ }, { "name": "grants", - "description": "Fetch a page of grants.", + "description": "Fetch a paginated list of grants.", "args": [ { "name": "after", - "description": "Paginating forwards: the cursor before the the requested page.", + "description": "Forward pagination: Cursor (grant ID) to start retrieving grants after this point.", "type": { "kind": "SCALAR", "name": "String", @@ -941,7 +941,7 @@ }, { "name": "before", - "description": "Paginating backwards: the cursor after the the requested page.", + "description": "Backward pagination: Cursor (grant ID) to start retrieving grants before this point.", "type": { "kind": "SCALAR", "name": "String", @@ -953,7 +953,7 @@ }, { "name": "filter", - "description": "Filter grants based on specific criteria.", + "description": "Filter grants based on specified criteria such as ID, state, or finalization reason.", "type": { "kind": "INPUT_OBJECT", "name": "GrantFilter", @@ -965,7 +965,7 @@ }, { "name": "first", - "description": "Paginating forwards: The first **n** elements from the page.", + "description": "Forward pagination: Limit the result to the first **n** grants after the `after` cursor.", "type": { "kind": "SCALAR", "name": "Int", @@ -977,7 +977,7 @@ }, { "name": "last", - "description": "Paginating backwards: The last **n** elements from the page.", + "description": "Backward pagination: Limit the result to the last **n** grants before the `before` cursor.", "type": { "kind": "SCALAR", "name": "Int", @@ -989,7 +989,7 @@ }, { "name": "sortOrder", - "description": "Ascending or descending order of creation.", + "description": "Specify the sort order of grants based on their creation date, either ascending or descending.", "type": { "kind": "ENUM", "name": "SortOrder", @@ -1026,7 +1026,7 @@ "inputFields": [ { "name": "grantId", - "description": null, + "description": "Unique identifier of the grant to revoke.", "type": { "kind": "NON_NULL", "name": null, @@ -1052,7 +1052,7 @@ "fields": [ { "name": "id", - "description": null, + "description": "Unique identifier of the revoked grant.", "args": [], "type": { "kind": "NON_NULL", @@ -1082,13 +1082,13 @@ "enumValues": [ { "name": "ASC", - "description": "Choose ascending order for results.", + "description": "Sort the results in ascending order.", "isDeprecated": false, "deprecationReason": null }, { "name": "DESC", - "description": "Choose descending order for results.", + "description": "Sort the results in descending order.", "isDeprecated": false, "deprecationReason": null } @@ -1108,7 +1108,7 @@ { "kind": "SCALAR", "name": "UInt8", - "description": null, + "description": "The `UInt8` scalar type represents unsigned 8-bit whole numeric values, ranging from 0 to 255.", "fields": null, "inputFields": null, "interfaces": null, @@ -1118,7 +1118,7 @@ { "kind": "SCALAR", "name": "UInt64", - "description": null, + "description": "The `UInt64` scalar type represents unsigned 64-bit whole numeric values. It is capable of handling values that are larger than the JavaScript `Number` type limit (greater than 2^53).", "fields": null, "inputFields": null, "interfaces": null, diff --git a/packages/auth/src/graphql/generated/graphql.ts b/packages/auth/src/graphql/generated/graphql.ts index dfc10637f5..3926b739d1 100644 --- a/packages/auth/src/graphql/generated/graphql.ts +++ b/packages/auth/src/graphql/generated/graphql.ts @@ -14,114 +14,130 @@ export type Scalars = { Boolean: { input: boolean; output: boolean; } Int: { input: number; output: number; } Float: { input: number; output: number; } + /** The `UInt8` scalar type represents unsigned 8-bit whole numeric values, ranging from 0 to 255. */ UInt8: { input: any; output: any; } + /** The `UInt64` scalar type represents unsigned 64-bit whole numeric values. It is capable of handling values that are larger than the JavaScript `Number` type limit (greater than 2^53). */ UInt64: { input: any; output: any; } }; export type Access = Model & { __typename?: 'Access'; - /** Access action (create, read, list or complete) */ + /** Actions allowed with this access. */ actions: Array>; - /** Date-time of creation */ + /** The date and time when the access was created. */ createdAt: Scalars['String']['output']; - /** Access id */ + /** Unique identifier of the access object. */ id: Scalars['ID']['output']; - /** Wallet address of a sub-resource (incoming payment, outgoing payment, or quote) */ + /** Wallet address of the sub-resource (incoming payment, outgoing payment, or quote). */ identifier?: Maybe; - /** Payment limits */ + /** Limits for an outgoing payment associated with this access. */ limits?: Maybe; - /** Access type (incoming payment, outgoing payment, or quote) */ + /** Type of access (incoming payment, outgoing payment, or quote). */ type: Scalars['String']['output']; }; export type FilterFinalizationReason = { + /** List of finalization reasons to include in the filter. */ in?: InputMaybe>; + /** List of finalization reasons to exclude in the filter. */ notIn?: InputMaybe>; }; export type FilterGrantState = { + /** List of states to include in the filter. */ in?: InputMaybe>; + /** List of states to exclude in the filter. */ notIn?: InputMaybe>; }; export type FilterString = { + /** Array of strings to filter by. */ in?: InputMaybe>; }; export type Grant = Model & { __typename?: 'Grant'; - /** Access details */ + /** Details of the access provided by the grant. */ access: Array; - /** Wallet address of the grantee's account */ + /** Wallet address of the grantee's account. */ client: Scalars['String']['output']; - /** Date-time of creation */ + /** The date and time when the grant was created. */ createdAt: Scalars['String']['output']; - /** Reason a grant was finalized */ + /** Specific outcome of a finalized grant, indicating whether the grant was issued, revoked, or rejected. */ finalizationReason?: Maybe; - /** Grant id */ + /** Unique identifier of the grant. */ id: Scalars['ID']['output']; - /** State of the grant */ + /** Current state of the grant. */ state: GrantState; }; export type GrantEdge = { __typename?: 'GrantEdge'; + /** A cursor for paginating through the grants. */ cursor: Scalars['String']['output']; + /** A grant node in the list. */ node: Grant; }; export type GrantFilter = { + /** Filter grants by their finalization reason. */ finalizationReason?: InputMaybe; + /** Filter grants by their unique identifier. */ identifier?: InputMaybe; + /** Filter grants by their state. */ state?: InputMaybe; }; export enum GrantFinalization { - /** grant was issued */ + /** The grant was issued successfully. */ Issued = 'ISSUED', - /** grant was rejected */ + /** The grant request was rejected. */ Rejected = 'REJECTED', - /** grant was revoked */ + /** The grant was revoked. */ Revoked = 'REVOKED' } export enum GrantState { - /** grant was approved */ + /** The grant request has been approved. */ Approved = 'APPROVED', - /** grant was finalized and no more access tokens or interactions can be made on it */ + /** The grant request has been finalized, and no more access tokens or interactions can be made. */ Finalized = 'FINALIZED', - /** grant request is awaiting interaction */ + /** The grant request is awaiting interaction. */ Pending = 'PENDING', - /** grant request is determining what state to enter next */ + /** The grant request is processing. */ Processing = 'PROCESSING' } export type GrantsConnection = { __typename?: 'GrantsConnection'; + /** A list of edges representing grants and cursors for pagination. */ edges: Array; + /** Information to aid in pagination. */ pageInfo: PageInfo; }; export type LimitData = { __typename?: 'LimitData'; - /** Amount to debit */ + /** Amount to debit. */ debitAmount?: Maybe; - /** Interval between payments */ + /** Interval between payments. */ interval?: Maybe; - /** Amount to receive */ + /** Amount to receive. */ receiveAmount?: Maybe; - /** Wallet address URL of the receiver */ + /** Wallet address URL of the receiver. */ receiver?: Maybe; }; export type Model = { + /** The date and time when the model was created. */ createdAt: Scalars['String']['output']; + /** Unique identifier for the model. */ id: Scalars['ID']['output']; }; export type Mutation = { __typename?: 'Mutation'; - /** Revoke Grant */ + /** Revoke an existing grant. */ revokeGrant: RevokeGrantMutationResponse; }; @@ -132,30 +148,31 @@ export type MutationRevokeGrantArgs = { export type PageInfo = { __typename?: 'PageInfo'; - /** Paginating forwards: the cursor to continue. */ + /** The cursor used to fetch the next page when paginating forward. */ endCursor?: Maybe; - /** Paginating forwards: Are there more pages? */ + /** Indicates if there are more pages when paginating forward. */ hasNextPage: Scalars['Boolean']['output']; - /** Paginating backwards: Are there more pages? */ + /** Indicates if there are more pages when paginating backward. */ hasPreviousPage: Scalars['Boolean']['output']; - /** Paginating backwards: the cursor to continue. */ + /** The cursor used to fetch the next page when paginating backward. */ startCursor?: Maybe; }; export type PaymentAmount = { __typename?: 'PaymentAmount'; - /** [ISO 4217 currency code](https://en.wikipedia.org/wiki/ISO_4217), e.g. `USD` */ + /** Should be an ISO 4217 currency code whenever possible, e.g. `USD`. For more information, refer to [assets](https://rafiki.dev/overview/concepts/accounting/#assets). */ assetCode: Scalars['String']['output']; - /** Difference in orders of magnitude between the standard unit of an asset and a corresponding fractional unit */ + /** Difference in orders of magnitude between the standard unit of an asset and a corresponding fractional unit. */ assetScale: Scalars['UInt8']['output']; + /** The value of the payment amount. */ value: Scalars['UInt64']['output']; }; export type Query = { __typename?: 'Query'; - /** Fetch a grant */ + /** Fetch a specific grant by its ID. */ grant: Grant; - /** Fetch a page of grants. */ + /** Fetch a paginated list of grants. */ grants: GrantsConnection; }; @@ -175,18 +192,20 @@ export type QueryGrantsArgs = { }; export type RevokeGrantInput = { + /** Unique identifier of the grant to revoke. */ grantId: Scalars['String']['input']; }; export type RevokeGrantMutationResponse = { __typename?: 'RevokeGrantMutationResponse'; + /** Unique identifier of the revoked grant. */ id: Scalars['ID']['output']; }; export enum SortOrder { - /** Choose ascending order for results. */ + /** Sort the results in ascending order. */ Asc = 'ASC', - /** Choose descending order for results. */ + /** Sort the results in descending order. */ Desc = 'DESC' } diff --git a/packages/auth/src/graphql/schema.graphql b/packages/auth/src/graphql/schema.graphql index a80d651089..0511ff0add 100644 --- a/packages/auth/src/graphql/schema.graphql +++ b/packages/auth/src/graphql/schema.graphql @@ -1,158 +1,182 @@ type Query { - "Fetch a page of grants." + "Fetch a paginated list of grants." grants( - "Paginating forwards: the cursor before the the requested page." + "Forward pagination: Cursor (grant ID) to start retrieving grants after this point." after: String - "Paginating backwards: the cursor after the the requested page." + "Backward pagination: Cursor (grant ID) to start retrieving grants before this point." before: String - "Paginating forwards: The first **n** elements from the page." + "Forward pagination: Limit the result to the first **n** grants after the `after` cursor." first: Int - "Paginating backwards: The last **n** elements from the page." + "Backward pagination: Limit the result to the last **n** grants before the `before` cursor." last: Int - "Filter grants based on specific criteria." + "Filter grants based on specified criteria such as ID, state, or finalization reason." filter: GrantFilter - "Ascending or descending order of creation." + "Specify the sort order of grants based on their creation date, either ascending or descending." sortOrder: SortOrder ): GrantsConnection! - "Fetch a grant" - grant(id: ID!): Grant! + "Fetch a specific grant by its ID." + grant("Unique identifier of the grant." id: ID!): Grant! } type Mutation { - "Revoke Grant" + "Revoke an existing grant." revokeGrant(input: RevokeGrantInput!): RevokeGrantMutationResponse! } type PageInfo { - "Paginating forwards: the cursor to continue." + "The cursor used to fetch the next page when paginating forward." endCursor: String - "Paginating forwards: Are there more pages?" + "Indicates if there are more pages when paginating forward." hasNextPage: Boolean! - "Paginating backwards: Are there more pages?" + "Indicates if there are more pages when paginating backward." hasPreviousPage: Boolean! - "Paginating backwards: the cursor to continue." + "The cursor used to fetch the next page when paginating backward." startCursor: String } type GrantsConnection { + "Information to aid in pagination." pageInfo: PageInfo! + "A list of edges representing grants and cursors for pagination." edges: [GrantEdge!]! } type GrantEdge { + "A grant node in the list." node: Grant! + "A cursor for paginating through the grants." cursor: String! } input GrantFilter { + "Filter grants by their unique identifier." identifier: FilterString + "Filter grants by their state." state: FilterGrantState + "Filter grants by their finalization reason." finalizationReason: FilterFinalizationReason } input FilterString { + "Array of strings to filter by." in: [String!] } input FilterGrantState { + "List of states to include in the filter." in: [GrantState!] + "List of states to exclude in the filter." notIn: [GrantState!] } input FilterFinalizationReason { + "List of finalization reasons to include in the filter." in: [GrantFinalization!] + "List of finalization reasons to exclude in the filter." notIn: [GrantFinalization!] } input RevokeGrantInput { + "Unique identifier of the grant to revoke." grantId: String! } interface Model { + "Unique identifier for the model." id: ID! + "The date and time when the model was created." createdAt: String! } type Grant implements Model { - "Grant id" + "Unique identifier of the grant." id: ID! - "Wallet address of the grantee's account" + "Wallet address of the grantee's account." client: String! - "Access details" + "Details of the access provided by the grant." access: [Access!]! - "State of the grant" + "Current state of the grant." state: GrantState! - "Reason a grant was finalized" + "Specific outcome of a finalized grant, indicating whether the grant was issued, revoked, or rejected." finalizationReason: GrantFinalization - "Date-time of creation" + "The date and time when the grant was created." createdAt: String! } type Access implements Model { - "Access id" + "Unique identifier of the access object." id: ID! - "Wallet address of a sub-resource (incoming payment, outgoing payment, or quote)" + "Wallet address of the sub-resource (incoming payment, outgoing payment, or quote)." identifier: String - "Access type (incoming payment, outgoing payment, or quote)" + "Type of access (incoming payment, outgoing payment, or quote)." type: String! - "Access action (create, read, list or complete)" + "Actions allowed with this access." actions: [String]! - "Payment limits" + "Limits for an outgoing payment associated with this access." limits: LimitData - "Date-time of creation" + "The date and time when the access was created." createdAt: String! } type LimitData { - "Wallet address URL of the receiver" + "Wallet address URL of the receiver." receiver: String - "Amount to debit" + "Amount to debit." debitAmount: PaymentAmount - "Amount to receive" + "Amount to receive." receiveAmount: PaymentAmount - "Interval between payments" + "Interval between payments." interval: String } type PaymentAmount { + "The value of the payment amount." value: UInt64! - "[ISO 4217 currency code](https://en.wikipedia.org/wiki/ISO_4217), e.g. `USD`" + "Should be an ISO 4217 currency code whenever possible, e.g. `USD`. For more information, refer to [assets](https://rafiki.dev/overview/concepts/accounting/#assets)." assetCode: String! - "Difference in orders of magnitude between the standard unit of an asset and a corresponding fractional unit" + "Difference in orders of magnitude between the standard unit of an asset and a corresponding fractional unit." assetScale: UInt8! } type RevokeGrantMutationResponse { + "Unique identifier of the revoked grant." id: ID! } enum GrantState { - "grant request is determining what state to enter next" + "The grant request is processing." PROCESSING - "grant request is awaiting interaction" + "The grant request is awaiting interaction." PENDING - "grant was approved" + "The grant request has been approved." APPROVED - "grant was finalized and no more access tokens or interactions can be made on it" + "The grant request has been finalized, and no more access tokens or interactions can be made." FINALIZED } enum GrantFinalization { - "grant was issued" + "The grant was issued successfully." ISSUED - "grant was revoked" + "The grant was revoked." REVOKED - "grant was rejected" + "The grant request was rejected." REJECTED } enum SortOrder { - "Choose ascending order for results." + "Sort the results in ascending order." ASC - "Choose descending order for results." + "Sort the results in descending order." DESC } +""" +The `UInt8` scalar type represents unsigned 8-bit whole numeric values, ranging from 0 to 255. +""" scalar UInt8 + +""" +The `UInt64` scalar type represents unsigned 64-bit whole numeric values. It is capable of handling values that are larger than the JavaScript `Number` type limit (greater than 2^53). +""" scalar UInt64 diff --git a/packages/auth/src/openapi/specs/id-provider.yaml b/packages/auth/src/openapi/specs/id-provider.yaml index ad5e1afd7f..d8ab1937f7 100644 --- a/packages/auth/src/openapi/specs/id-provider.yaml +++ b/packages/auth/src/openapi/specs/id-provider.yaml @@ -89,6 +89,8 @@ paths: description: Client finish endpoint '401': description: Unauthorized + '404': + description: Not Found description: "To finish the user interaction for grant approval, this endpoint redirects the user to the client's finish url." parameters: - schema: diff --git a/packages/backend/src/config/app.ts b/packages/backend/src/config/app.ts index 29a3c7070a..46e2d02dff 100644 --- a/packages/backend/src/config/app.ts +++ b/packages/backend/src/config/app.ts @@ -187,7 +187,11 @@ export const Config = { 'INCOMING_PAYMENT_EXPIRY_MAX_MS', 2592000000 ), // 30 days - enableSpspPaymentPointers: envBool('ENABLE_SPSP_PAYMENT_POINTERS', true) + enableSpspPaymentPointers: envBool('ENABLE_SPSP_PAYMENT_POINTERS', true), + maxOutgoingPaymentRetryAttempts: envInt( + 'MAX_OUTGOING_PAYMENT_RETRY_ATTEMPTS', + 5 + ) } function parseRedisTlsConfig( diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index dfa64c0ef4..a178e8b345 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -464,6 +464,7 @@ export function initIocContainer( container.singleton('outgoingPaymentService', async (deps) => { return await createOutgoingPaymentService({ + config: await deps.use('config'), logger: await deps.use('logger'), knex: await deps.use('knex'), accountingService: await deps.use('accountingService'), diff --git a/packages/backend/src/open_payments/payment/incoming/model.ts b/packages/backend/src/open_payments/payment/incoming/model.ts index b22f086af7..f68a254357 100644 --- a/packages/backend/src/open_payments/payment/incoming/model.ts +++ b/packages/backend/src/open_payments/payment/incoming/model.ts @@ -155,8 +155,7 @@ export class IncomingPayment incomingPayment = await IncomingPayment.query() .patchAndFetchById(this.id, { state: IncomingPaymentState.Completed, - // Add 30 seconds to allow a prepared (but not yet fulfilled/rejected) packet to finish before sending webhook event. - processAt: new Date(Date.now() + 30_000) + processAt: new Date() }) .whereNotIn('state', [ IncomingPaymentState.Expired, diff --git a/packages/backend/src/open_payments/payment/incoming/service.test.ts b/packages/backend/src/open_payments/payment/incoming/service.test.ts index d26e5c4ac4..90465420ba 100644 --- a/packages/backend/src/open_payments/payment/incoming/service.test.ts +++ b/packages/backend/src/open_payments/payment/incoming/service.test.ts @@ -587,8 +587,7 @@ describe('Incoming Payment Service', (): void => { test('Sets state of fully paid incoming payment to "completed"', async (): Promise => { const now = new Date() - jest.useFakeTimers() - jest.setSystemTime(now) + jest.useFakeTimers({ now }) await expect( incomingPayment.onCredit({ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion @@ -597,7 +596,7 @@ describe('Incoming Payment Service', (): void => { ).resolves.toMatchObject({ id: incomingPayment.id, state: IncomingPaymentState.Completed, - processAt: new Date(now.getTime() + 30_000) + processAt: now }) await expect( incomingPaymentService.get({ @@ -605,7 +604,7 @@ describe('Incoming Payment Service', (): void => { }) ).resolves.toMatchObject({ state: IncomingPaymentState.Completed, - processAt: new Date(now.getTime() + 30_000) + processAt: now }) }) }) @@ -662,9 +661,8 @@ describe('Incoming Payment Service', (): void => { }) ).resolves.toBeUndefined() - const now = incomingPayment.expiresAt jest.useFakeTimers() - jest.setSystemTime(now) + jest.setSystemTime(incomingPayment.expiresAt) await expect(incomingPaymentService.processNext()).resolves.toBe( incomingPayment.id ) @@ -674,7 +672,7 @@ describe('Incoming Payment Service', (): void => { }) ).resolves.toMatchObject({ state: IncomingPaymentState.Expired, - processAt: new Date(now.getTime() + 30_000) + processAt: new Date() }) }) @@ -820,13 +818,14 @@ describe('Incoming Payment Service', (): void => { }) test('updates state of pending incoming payment to complete', async (): Promise => { const now = new Date() - jest.spyOn(global.Date, 'now').mockImplementation(() => now.valueOf()) + jest.useFakeTimers({ now }) + await expect( incomingPaymentService.complete(incomingPayment.id) ).resolves.toMatchObject({ id: incomingPayment.id, state: IncomingPaymentState.Completed, - processAt: new Date(now.getTime() + 30_000) + processAt: now }) await expect( incomingPaymentService.get({ @@ -834,7 +833,7 @@ describe('Incoming Payment Service', (): void => { }) ).resolves.toMatchObject({ state: IncomingPaymentState.Completed, - processAt: new Date(now.getTime() + 30_000) + processAt: now }) }) @@ -845,6 +844,9 @@ describe('Incoming Payment Service', (): void => { }) test('updates state of processing incoming payment to complete', async (): Promise => { + const now = new Date() + jest.useFakeTimers({ now }) + await incomingPayment.onCredit({ totalReceived: BigInt(100) }) @@ -860,7 +862,7 @@ describe('Incoming Payment Service', (): void => { ).resolves.toMatchObject({ id: incomingPayment.id, state: IncomingPaymentState.Completed, - processAt: new Date(incomingPayment.expiresAt.getTime()) + processAt: now }) await expect( incomingPaymentService.get({ @@ -868,7 +870,7 @@ describe('Incoming Payment Service', (): void => { }) ).resolves.toMatchObject({ state: IncomingPaymentState.Completed, - processAt: new Date(incomingPayment.expiresAt.getTime()) + processAt: now }) }) diff --git a/packages/backend/src/open_payments/payment/incoming/service.ts b/packages/backend/src/open_payments/payment/incoming/service.ts index 62a8a99547..2fac831885 100644 --- a/packages/backend/src/open_payments/payment/incoming/service.ts +++ b/packages/backend/src/open_payments/payment/incoming/service.ts @@ -268,8 +268,7 @@ async function handleExpired( ) await incomingPayment.$query(deps.knex).patch({ state: IncomingPaymentState.Expired, - // Add 30 seconds to allow a prepared (but not yet fulfilled/rejected) packet to finish before sending webhook event. - processAt: new Date(Date.now() + 30_000) + processAt: new Date() }) } else { deps.logger.debug({ amountReceived }, 'deleting expired incoming payment') @@ -437,7 +436,7 @@ async function completeIncomingPayment( } await payment.$query(trx).patch({ state: IncomingPaymentState.Completed, - processAt: new Date(Date.now() + 30_000) + processAt: new Date() }) return await addReceivedAmount(deps, payment) }) diff --git a/packages/backend/src/open_payments/payment/outgoing/service.ts b/packages/backend/src/open_payments/payment/outgoing/service.ts index 336444e518..2c9ad6043f 100644 --- a/packages/backend/src/open_payments/payment/outgoing/service.ts +++ b/packages/backend/src/open_payments/payment/outgoing/service.ts @@ -42,6 +42,7 @@ import { QuoteService } from '../../quote/service' import { isQuoteError } from '../../quote/errors' import { Pagination, SortOrder } from '../../../shared/baseModel' import { FilterString } from '../../../shared/filters' +import { IAppConfig } from '../../../config/app' export interface OutgoingPaymentService extends WalletAddressSubresourceService { @@ -59,6 +60,7 @@ export interface OutgoingPaymentService } export interface ServiceDependencies extends BaseService { + config: IAppConfig knex: TransactionOrKnex accountingService: AccountingService receiverService: ReceiverService diff --git a/packages/backend/src/open_payments/payment/outgoing/worker.ts b/packages/backend/src/open_payments/payment/outgoing/worker.ts index 0df698f960..9ca47b8938 100644 --- a/packages/backend/src/open_payments/payment/outgoing/worker.ts +++ b/packages/backend/src/open_payments/payment/outgoing/worker.ts @@ -10,8 +10,6 @@ import { trace, Span } from '@opentelemetry/api' // First retry waits 10 seconds, second retry waits 20 (more) seconds, etc. export const RETRY_BACKOFF_SECONDS = 10 -const MAX_STATE_ATTEMPTS = 5 - // Returns the id of the processed payment (if any). export async function processPendingPayment( deps_: ServiceDependencies @@ -94,7 +92,10 @@ async function onLifecycleError( const error = typeof err === 'string' ? err : err.message const stateAttempts = payment.stateAttempts + 1 - if (stateAttempts < MAX_STATE_ATTEMPTS && isRetryableError(err)) { + if ( + stateAttempts < deps.config.maxOutgoingPaymentRetryAttempts && + isRetryableError(err) + ) { deps.logger.warn( { state: payment.state, error, stateAttempts }, 'payment lifecycle failed; retrying' diff --git a/packages/backend/src/payment-method/ilp/connector/core/middleware/account.ts b/packages/backend/src/payment-method/ilp/connector/core/middleware/account.ts index 12f74deb2e..e918aec4ba 100644 --- a/packages/backend/src/payment-method/ilp/connector/core/middleware/account.ts +++ b/packages/backend/src/payment-method/ilp/connector/core/middleware/account.ts @@ -87,10 +87,6 @@ export function createAccountMiddleware(serverAddress: string): ILPMiddleware { LiquidityAccountType.INCOMING ) } - ctx.services.logger.debug( - { incomingPaymentId: incomingPayment.id }, - 'destination account is incoming payment' - ) return incomingPayment } // Open Payments SPSP fallback account @@ -104,20 +100,12 @@ export function createAccountMiddleware(serverAddress: string): ILPMiddleware { LiquidityAccountType.WEB_MONETIZATION ) } - ctx.services.logger.debug( - { walletAddressId: walletAddress.id }, - 'destination account is wallet address' - ) return walletAddress } } const address = ctx.request.prepare.destination const peer = await peers.getByDestinationAddress(address) if (peer) { - ctx.services.logger.debug( - { peerId: peer.id }, - 'destination account is peer' - ) return peer } if ( diff --git a/packages/documentation/src/content/docs/admin/admin-user-guide.mdx b/packages/documentation/src/content/docs/admin/admin-user-guide.mdx index cc9fe587ce..30e93626ae 100644 --- a/packages/documentation/src/content/docs/admin/admin-user-guide.mdx +++ b/packages/documentation/src/content/docs/admin/admin-user-guide.mdx @@ -47,7 +47,15 @@ After running the `invite-user` script, the script generates a recovery link tha /> :::note -The invitation link is single-use for security purposes. Once accessed, it becomes invalid. If sending the link through Slack, ensure you format it as code by placing it inside backticks (\`) to prevent Slack from automatically previewing the link, which would invalidate it. Example: \``http://localhost:4433/self-service/recovery?flow=116250ee-07bd-4b5c-a98e-87406192bb4b&token=miv0yZ7DFKKw8RyBBQvWoOsTRa2TVuZm`\`. +The invitation link is single-use for security purposes. Once accessed, it becomes invalid. + +If sending the link through Slack, ensure you format it as code by placing it inside backticks (\`) to prevent Slack from automatically previewing the link, which would invalidate it. For example: + +{/* prettier-ignore */} +```js wrap +`http://localhost:4433/self-service/recovery?flow=116250ee-07bd-4b5c-a98e-87406192bb4b&token=miv0yZ7DFKKw8RyBBQvWoOsTRa2TVuZm` +``` + ::: #### Generate a recovery link diff --git a/packages/documentation/src/content/docs/admin/manage-liquidity.mdx b/packages/documentation/src/content/docs/admin/manage-liquidity.mdx index 906ff80d44..a5963dc761 100644 --- a/packages/documentation/src/content/docs/admin/manage-liquidity.mdx +++ b/packages/documentation/src/content/docs/admin/manage-liquidity.mdx @@ -52,6 +52,8 @@ mutation DepositAssetLiquidity($input: DepositAssetLiquidityInput!) { +
+ | Variable | Description | | ---------------- | -------------------------------------------------------------------------- | | `assetID` | The id of the asset to deposit liquidity into | @@ -59,6 +61,8 @@ mutation DepositAssetLiquidity($input: DepositAssetLiquidityInput!) { | `id` | The id of the transfer (deposit) | | `idempotencyKey` | Unique key to ensure duplicate or retried requests are processed only once | +
+ If the asset liquidity deposit was successful, `DepositAssetLiquidity` returns `true`. ### Withdraw asset liquidity using the `CreateAssetLiquidityWithdrawal` mutation @@ -98,6 +102,8 @@ mutation CreateAssetLiquidityWithdrawal( +
+ | Variable | Description | | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | The id of the transfer (withdrawal) | @@ -106,6 +112,8 @@ mutation CreateAssetLiquidityWithdrawal( | `idempotencyKey` | Unique key to ensure duplicate or retried requests are processed only once | | `timeoutSeconds` | Interval, in seconds, after a pending transfer is initiated at which it can be posted or voided (zero denotes a no timeout, single-phase posted transfer) | +
+ If the asset liquidity withdrawal was successful, `CreateAssetLiquidityWithdrawal` returns `true`. ### Deposit and withdraw asset liquidity using Rafiki Admin @@ -154,6 +162,8 @@ mutation DepositPeerLiquidity($input: DepositPeerLiquidityInput!) { +
+ | Variable | Description | | ---------------- | -------------------------------------------------------------------------- | | `id` | The id of the transfer (deposit) | @@ -161,6 +171,8 @@ mutation DepositPeerLiquidity($input: DepositPeerLiquidityInput!) { | `amount` | Amount of liquidity to deposit | | `idempotencyKey` | Unique key to ensure duplicate or retried requests are processed only once | +
+ If the peer liquidity deposit was successful, `DepositPeerLiquidity` returns `true`. ### Withdraw peer liquidity using the `CreatePeerLiquidityWithdrawal` mutation @@ -200,6 +212,8 @@ mutation CreatePeerLiquidityWithdrawal( +
+ | Variable | Description | | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | The id of the transfer (withdrawal) | @@ -208,6 +222,8 @@ mutation CreatePeerLiquidityWithdrawal( | `idempotencyKey` | Unique key to ensure duplicate or retried requests are processed only once | | `timeoutSeconds` | Interval, in seconds, after a pending transfer is initiated at which it can be posted or voided (zero denotes a no timeout, single-phase posted transfer) | +
+ If the peer liquidity withdrawal was successful, `CreatePeerLiquidityWithdrawal` returns `true`. ### Deposit and withdraw peer liquidity using Rafiki Admin @@ -253,12 +269,16 @@ mutation CreateIncomingPaymentWithdrawal( +
+ | Variable | Description | | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | `incomingPaymentId` | The id of the incoming payment to withdraw from | | `idempotencyKey` | Unique key to ensure duplicate or retried requests are processed only once | | `timeoutSeconds` | Interval, in seconds, after a pending transfer is initiated at which it can be posted or voided (zero denotes a no timeout, single-phase posted transfer) | +
+ If the incoming payment liquidity withdrawal was successful, `CreateIncomingPaymentWithdrawal` returns `true`. ### Deposit outgoing payment liquidity using the `DepositOutgoingPaymentLiquidity` mutation @@ -295,11 +315,15 @@ mutation DepositOutgoingPaymentLiquidity( +
+ | Variable | Description | | ------------------- | -------------------------------------------------------------------------- | | `outgoingPaymentId` | The id of the outgoing payment to deposit into | | `idempotencyKey` | Unique key to ensure duplicate or retried requests are processed only once | +
+ If the outgoing payment liquidity deposit was successful, `DepositOutgoingPaymentLiquidity` returns `true`. ### Withdraw outgoing payment liquidity using the `CreateOutgoingPaymentWithdrawal` mutation @@ -337,12 +361,16 @@ mutation CreateOutgoingPaymentWithdrawal( +
+ | Variable | Description | | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | `outgoingPaymentId` | The id of the outgoing payment to withdraw from | | `idempotencyKey` | Unique key to ensure duplicate or retried requests are processed only once | | `timeoutSeconds` | Interval, in seconds, after a pending transfer is initiated at which it can be posted or voided (zero denotes a no timeout, single-phase posted transfer) | +
+ If the outgoing payment liquidity withdrawal was successful, `CreateOutgoingPaymentWithdrawal` returns `true`. ## Two-phase withdrawals @@ -393,11 +421,15 @@ mutation PostLiquidityWithdrawal($input: PostLiquidityWithdrawalInput!) { +
+ | Variable | Description | | ---------------- | -------------------------------------------------------------------------- | | `withdrawalId` | The id of the liquidity withdrawal to post | | `idempotencyKey` | Unique key to ensure duplicate or retried requests are processed only once | +
+ If the liquidity withdrawal was successfully posted, `PostLiquidityWithdrawal` returns `true`. ### Void and rollback an unsuccessful transfer using the `VoidLiquidityWithdrawal` mutation @@ -432,9 +464,13 @@ mutation VoidLiquidityWithdrawal($input: VoidLiquidityWithdrawalInput!) { +
+ | Variable | Description | | ---------------- | -------------------------------------------------------------------------- | | `withdrawalId` | The id of the liquidity withdrawal to void | | `idempotencyKey` | Unique key to ensure duplicate or retried requests are processed only once | +
+ If the liquidity withdrawal was successfully voided and rolled back, `VoidLiquidityWithdrawal` returns `true`. diff --git a/packages/documentation/src/content/docs/admin/manage-peering.mdx b/packages/documentation/src/content/docs/admin/manage-peering.mdx index 6fab621d7d..c01db8b25f 100644 --- a/packages/documentation/src/content/docs/admin/manage-peering.mdx +++ b/packages/documentation/src/content/docs/admin/manage-peering.mdx @@ -66,6 +66,8 @@ mutation CreatePeer($input: CreatePeerInput!) { +
+ | Variable | Description | Required | | -------------------------- | --------------------------------------------------------------------------------------------------------------------------- | -------- | | `assetID` | The ID of the asset that you and your peer will use to ultimately settle your net obligations outside of Interledger. | Y | @@ -76,6 +78,8 @@ mutation CreatePeer($input: CreatePeerInput!) { | `http.outgoing.authtoken` | The token that you will use to present to your peer and connect to and send packets to their Rafiki instance. | Y | | `initialLiquidity` | Initial amount of liquidity to deposit for peer. Liquidity can also be deposited using the `DepositPeerLiquidity` mutation. | N | +
+ ```json diff --git a/packages/documentation/src/content/docs/integration/playground/autopeering.mdx b/packages/documentation/src/content/docs/integration/playground/autopeering.mdx index c2ceaa4fc6..8de006c3c5 100644 --- a/packages/documentation/src/content/docs/integration/playground/autopeering.mdx +++ b/packages/documentation/src/content/docs/integration/playground/autopeering.mdx @@ -4,7 +4,7 @@ title: Auto-Peering with the Test Network import { LinkOut } from '@interledger/docs-design-system' -You can start one local instance of Rafiki and peer it automatically with the remote Test Network by running the following commands: +You can start one local instance of Rafiki and peer it automatically with the remote Test Network by running the following commands: ```sh ## using Tigerbeetle DB @@ -13,7 +13,7 @@ pnpm localenv:compose:autopeer pnpm localenv:compose:psql:autopeer ``` -The mock account servicing entity, Cloud Nine Wallet, in your local Rafiki instance will automatically peer with the remote Test Network instance. The required services will be exposed externally using the localtunnel package. +The mock account servicing entity, Cloud Nine Wallet, in your local Rafiki instance will automatically peer with the remote Test Network instance. The required services will be exposed externally using the localtunnel package. The exposed ports are: diff --git a/packages/documentation/src/content/docs/integration/playground/overview.mdx b/packages/documentation/src/content/docs/integration/playground/overview.mdx index cbc4caedf6..59c7d41c25 100644 --- a/packages/documentation/src/content/docs/integration/playground/overview.mdx +++ b/packages/documentation/src/content/docs/integration/playground/overview.mdx @@ -2,6 +2,8 @@ title: Overview --- +import { LargeImg } from '@interledger/docs-design-system' + import { LinkOut } from '@interledger/docs-design-system' import { CodeBlock } from '@interledger/docs-design-system' @@ -10,6 +12,8 @@ The Local Playground provides a suite of packages that, together, mock an accoun This suite of packages includes: +
+ | Package name | Services | | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | | [`backend`](/integration/services/backend-service) |
  • SPSP
  • Open Payments APIs
  • GraphQL Admin APIs
  • STREAM endpoint
| @@ -17,6 +21,8 @@ This suite of packages includes: | `mock-account-servicing-entity` | mocks an account servicing entity | | [`frontend`](/integration/services/frontend-service) | Remix app to expose a UI for Rafiki admin management via interaction with the Backend Admin APIs | +
+ These packages depend on the following databases: Overview of components + #### Mock account servicing entity 1 - Cloud Nine Wallet diff --git a/packages/documentation/src/content/docs/integration/playground/testnet.mdx b/packages/documentation/src/content/docs/integration/playground/testnet.mdx index 3c81e2025a..bdf07901eb 100644 --- a/packages/documentation/src/content/docs/integration/playground/testnet.mdx +++ b/packages/documentation/src/content/docs/integration/playground/testnet.mdx @@ -12,8 +12,12 @@ The [Local Playground](/integration/playground/overview/) is not the only way to The current applications include: -- An Interledger test wallet -- An e-commerce application +- + An Interledger test wallet + +- + An e-commerce application + ## Peering with the Test Network diff --git a/packages/documentation/src/content/docs/integration/prod/docker-compose.mdx b/packages/documentation/src/content/docs/integration/prod/docker-compose.mdx index b6f338c3a2..c0c3fd7970 100644 --- a/packages/documentation/src/content/docs/integration/prod/docker-compose.mdx +++ b/packages/documentation/src/content/docs/integration/prod/docker-compose.mdx @@ -2,56 +2,25 @@ title: Docker Compose --- -import { LinkOut } from '@interledger/docs-design-system' +This is an example deployment of Rafiki on a Linux virtual machine using nginx as a reverse proxy. You must change the values in the examples appropriate for your environment. -This guide is an example of deploying Rafiki using Docker Compose with Nginx as a reverse proxy on a virtual machine (VM) in a cloud environment. This guide also uses Certbot to generate Let’s Encrypt TLS certificates to secure exposed ports using HTTPS. +## Virtual Machine preparation -## Prerequisites - -### Domain and subdomains setup - -We will map the [Open Payments resource server](/integration/services/backend-service#open-payments) to your domain, and the [ILP Connector](/integration/services/backend-service#interledger-connector), [Open Payments auth server](/integration/services/auth-service), and [Admin UI](/integration/services/frontend-service) to subdomains. Using the DNS host of your choice, set up your domain and subdomains according to the following recommended convention: - -
- -| service | function | URL | example | -| ----------------------------- | --------------------------------------------------------------------------- | ------------ | ------------------ | -| Open Payments resource server | Exposes the Open Payments APIs | DOMAIN | myrafiki.com | -| ILP Connector | Exposes an ILP connector to send and receive ILP packets between peers | ilp.DOMAIN | ilp.myrafiki.com | -| Open Payments auth server | Exposes a reference implementation of an Open Payments authorization server | auth.DOMAIN | auth.myrafiki.com | -| Admin UI | Exposes an Admin UI to manage Rafiki | admin.DOMAIN | admin.myrafiki.com | - -
- -:::note -The example domain and subdomain values are for demonstration purposes only. You must use the actual domain names that you set up with your DNS host. -::: - -### VM specifications - -A general purpose VM with the following specifications is sufficient for running Rafiki via Docker: - -- OS: Linux distro -- RAM: 4GB -- vCPUs: 2 - -### Install Nginx and Certbot - -Once you have provisioned the VM in your cloud environment, install Nginx along with Certbot: +Deploy the virtual machine: ```sh sudo apt update && sudo apt install nginx certbot python3-certbot-nginx ``` -### Domain preparation +## Domain preparation -Generate the Let’s Encrypt certificates using Certbot: +Generate the Let's Encrypt certificates: ```sh certbot certonly --manual --preferred-challenges=dns --email EMAIL --server https://acme-v02.api.letsencrypt.org/directory --agree-tos -d DOMAIN ``` -:::caution[Update TXT record] +:::note Domain can be in wildcard format. You will also need to update the TXT record in this step. ::: @@ -60,257 +29,43 @@ As Let's Encrypt certificates are valid for 90 days, you must set up a cron proc ```sh crontab -e ​ -0 3 * * * certbot renew +0 3 * * * crontab renew ``` -### Update DNS records +## Update DNS records -Next update the DNS records (A records) to point to the static external IP address of the virtual machine: +Next update the DNS records to point to the static external IP address of the volumes:
-| service | URL | example | -| ----------------------------- | ------------ | ------------------ | -| Open Payments resource server | DOMAIN | myrafiki.com | -| ILP Connector | ilp.DOMAIN | ilp.myrafiki.com | -| Open Payments auth server | auth.DOMAIN | auth.myrafiki.com | -| Admin UI | admin.DOMAIN | admin.myrafiki.com | +| service | URL | example | +| --------- | ---------------- | ---------------------- | +| admin | admin.DOMAIN | admin.myrafiki.com | +| auth | auth.DOMAIN | auth.myrafiki.com | +| connector | connector.DOMAIN | connector.myrafiki.com | +| ilp | ilp.DOMAIN | ilp.myrafiki.com |
-:::note -The example domain and subdomain values are for demonstration purposes only. You must use the actual domain names that you set up with your DNS host. -::: - -### Install Docker Engine and Docker Compose plugin - -Set up Docker's `apt` respository on your Linux-based VM: - -```sh -# Add Docker's official GPG key: -sudo apt-get update -sudo apt-get install ca-certificates curl -sudo install -m 0755 -d /etc/apt/keyrings -sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc -sudo chmod a+r /etc/apt/keyrings/docker.asc - -# Add the repository to Apt sources: -echo \ - "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ - $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ - sudo tee /etc/apt/sources.list.d/docker.list > /dev/null -sudo apt-get update - -``` - -Install the Docker packages: - -```sh -sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -``` - -## Configure compose file - -Update the variables in the following compose file with values relevant to your environment and system. Refer to the [environment variables page](/resources/environment-variables/) for details. - -:::note[Update compose file] -You must change the values enclosed within curly brackets {} in the following compose file. -::: - -```sh -name: 'my-rafiki' -services: - rafiki-auth: - image: ghcr.io/interledger/rafiki-auth:v1.0.0-alpha. - container_name: rafiki-auth - environment: - AUTH_DATABASE_URL: {postgresql://...} - AUTH_SERVER_URL: {https://auth.myrafiki.com} - ADMIN_PORT: 3003 - AUTH_PORT: 3006 - INTROSPECTION_PORT: 3007 - INTERACTION_PORT: 3009 - COOKIE_KEY: {...} - IDENTITY_SERVER_SECRET: {...} - IDENTITY_SERVER_URL: {https://idp.mysystem.com} - REDIS_URL: {redis://...} - TRUST_PROXY: true - depends_on: - - postgres - networks: - - rafiki - ports: - - '3003:3003' - - '3006:3006' - - '3007:3007' - - '3009:3009' - restart: always - - rafiki-backend: - image: ghcr.io/interledger/rafiki-backend:v1.0.0-alpha. - container_name: rafiki-backend - depends_on: - - postgres - - redis - environment: - AUTH_SERVER_GRANT_URL: {https://auth.myrafiki.com} - AUTH_SERVER_INTROSPECTION_URL: {https://auth.myrafiki.com/3007} - DATABASE_URL: {postgresql://...} - ILP_ADDRESS: {test.myrafiki} - ADMIN_PORT: 3001 - CONNECTOR_PORT: 3002 - OPEN_PAYMENTS_PORT: 3000 - OPEN_PAYMENTS_URL: {https://myrafiki.com} - REDIS_URL: {redis://...} - WALLET_ADDRESS_URL: {https://myrafiki.com/rafiki-instance} - WEBHOOK_URL: {https://mysystem.com/webhooks} - EXCHANGE_RATES_URL: {https://mysystem.com/rates} - ILP_CONNECTOR_URL: {https://ilp.myrafiki.com} - INSTANCE_NAME: {'My ASE name'} - TRUST_PROXY: true - KEY_ID: ... - USE_TIGERBEETLE: true - TIGERBEETLE_CLUSTER_ID: 0 - TIGERBEETLE_REPLICA_ADDRESSES: 10.5.0.50:4342 - networks: - - rafiki - ports: - - '3000:3000' - - '3001:3001' - - '3002:3002' - privileged: true - restart: always - volumes: - - ../temp/:/workspace/temp/ - - rafiki-frontend: - image: ghcr.io/interledger/rafiki-frontend:v1.0.0-alpha. - container_name: rafiki-frontend - depends_on: - - rafiki-backend - environment: - PORT: 3005 - GRAPHQL_URL: {https://myrafiki.com:3001} - OPEN_PAYMENTS_URL: {https://myrafiki.com} - KRATOS_CONTAINER_PUBLIC_URL: {http://kratos:4433} - KRATOS_BROWSER_PUBLIC_URL: {https://admin.myrafiki.com/kratos} - KRATOS_ADMIN_URL: {http://kratos:4434/admin} - networks: - - rafiki - restart: always - privileged: true - ports: - - '3005:3005' - - tigerbeetle: - image: ghcr.io/tigerbeetle/tigerbeetle:0.15.4 - privileged: true - volumes: - - tigerbeetle-data:/var/lib/tigerbeetle - networks: - rafiki: - ipv4_address: 10.5.0.50 - entrypoint: - - /bin/sh - - -c - - | - set -ex - DATA_FILE=/var/lib/tigerbeetle/cluster_0_replica_0.tigerbeetle - set +e - ls $$DATA_FILE - DATA_FILE_EXISTS="$$?" - set -e - echo $$DATA_FILE_EXISTS - if [ "$$DATA_FILE_EXISTS" != 0 ]; then - ./tigerbeetle format --cluster=0 --replica=0 --replica-count=1 $$DATA_FILE; - fi - hostname -i - ls /var/lib/tigerbeetle - ./tigerbeetle start --addresses=0.0.0.0:4342 $$DATA_FILE - - postgres: - image: 'postgres:16' - container_name: postgres - environment: - POSTGRES_USER: ... - POSTGRES_PASSWORD: ... - networks: - - rafiki - restart: unless-stopped - volumes: - - pg-data:/var/lib/postgresql/data - - ../dbinit.sql:/docker-entrypoint-initdb.d/init.sql - - redis: - image: 'redis:7' - restart: unless-stopped - networks: - - rafiki - - kratos: - image: 'oryd/kratos:v1.2.0' - privileged: true - ports: - - '4433:4433' - volumes: - - ../entrypoint.sh:/entrypoint.sh - - ../identity.schema.json:/etc/config/kratos/identity.schema.json - - ./kratos.yml:/etc/config/kratos/kratos.yml - entrypoint: ['/entrypoint.sh'] - networks: - - rafiki - -networks: - testnet: - driver: bridge - ipam: - config: - - subnet: 10.5.0.0/24 - gateway: 10.5.0.1 - -volumes: - pg-data: - tigerbeetle-data: - -``` - -## Create Nginx config files +## Server preparation Create nginx configuration files for every exposed domain: -
- -| service | URL | example | Nginx config file | -| ----------------------------- | ------------ | ------------------ | --------------------------------------------------------------- | -| Open Payments resource server | DOMAIN | myrafiki.com | /etc/nginx/sites-available/open_payments_resource_server.config | -| ILP Connector | ilp.DOMAIN | ilp.myrafiki.com | /etc/nginx/sites-available/ilp.config | -| Open Payments auth server | auth.DOMAIN | auth.myrafiki.com | /etc/nginx/sites-available/open_payments_auth_server.config | -| Admin UI | admin.DOMAIN | admin.myrafiki.com | /etc/nginx/site-available/admin.config | - -
- -:::note -The example domain and subdomain values are for demonstration purposes only. You must use the actual domain names that you set up with your DNS host. -::: - -### Open Payments Resource Server (`backend` package) - -Using the editor of your choice, save the following file as `open_payments_resource_server.config` in the `/etc/nginx/sites-available` directory on your VM: +### Admin ```sh - server { - server_name myrafiki.com; + server_name admin.myrafiki.com; - listen 443 ssl; + listen 443 ssl; - ssl_certificate /etc/letsencrypt/live/myrafiki.com/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/myrafiki.com/privkey.pem; + ssl_certificate /etc/letsencrypt/live/admin.myrafiki.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/admin.myrafiki.com/privkey.pem; - include /etc/letsencrypt/options-ssl-nginx.conf; - ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; + include /etc/letsencrypt/options-ssl-nginx.conf; + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; - location / { + location / { proxy_http_version 1.1; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Upgrade ""; @@ -321,16 +76,16 @@ Using the editor of your choice, save the following file as `open_payments_resou proxy_pass_request_headers on; - proxy_pass http://localhost:3000; + proxy_pass http://localhost:4010; } } server { - server_name myrafiki.com; + server_name admin.myrafiki.com; listen 80; - if ($host = myrafiki.com) { + if ($host = admin.myrafiki.com) { return 301 https://$host$request_uri; } @@ -338,18 +93,16 @@ server { } ``` -### ILP Connector (`backend` package) - -Save the following file as `ilp.config` in the `/etc/nginx/sites-available` directory on your VM: +### Auth ```sh server { - server_name ilp.myrafiki.com; + server_name auth.myrafiki.com; listen 443 ssl; - ssl_certificate /etc/letsencrypt/live/ilp.myrafiki.com/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/ilp.myrafiki.com/privkey.pem; + ssl_certificate /etc/letsencrypt/live/auth.myrafiki.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/auth.myrafiki.com/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; @@ -370,29 +123,27 @@ server { } server { - server_name ilp.myrafiki.com; + server_name auth.myrafiki.com; listen 80; - if ($host = ilp.myrafiki.com) { - return 301 https://$host$request_uri; + if ($host = auth.myrafiki.com) { + return 301 https://$host$request_uri; } return 404; } ``` -### Open Payments Auth Server (`auth` package) - -Save the following file as `open_payments_auth_server.config` in the `/etc/nginx/sites-available` directory on your VM: +### Connector ```sh server { - server_name auth.myrafiki.com; + server_name connector.myrafiki.com; listen 443 ssl; - ssl_certificate /etc/letsencrypt/live/auth.myrafiki.com/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/auth.myrafiki.com/privkey.pem; + ssl_certificate /etc/letsencrypt/live/connector.myrafiki.com/fullchain.pem; # managed by Certbot + ssl_certificate_key /etc/letsencrypt/live/connector.myrafiki.com/privkey.pem; # managed by Certbot include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; @@ -408,35 +159,33 @@ server { proxy_pass_request_headers on; - proxy_pass http://localhost:3006; + proxy_pass http://localhost:3002; } } server { - server_name auth.myrafiki.com; + server_name connector.myrafiki.com; listen 80; - if ($host = auth.myrafiki.com) { - return 301 https://$host$request_uri; + if ($host = connector.myrafiki.com) { + return 301 https://$host$request_uri; } return 404; } ``` -### Admin (`frontend` package) - -Save the following file as `admin.config` in the `/etc/nginx/sites-available` directory on your VM: +### ILP ```sh server { - server_name admin.myrafiki.com; + server_name ilp.myrafiki.com; listen 443 ssl; - ssl_certificate /etc/letsencrypt/live/admin.myrafiki.com/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/admin.myrafiki.com/privkey.pem; + ssl_certificate /etc/letsencrypt/live/ilp.myrafiki.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/ilp.myrafiki.com/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; @@ -452,42 +201,19 @@ server { proxy_pass_request_headers on; - proxy_pass http://localhost:3005; + proxy_pass http://localhost:4000; } } server { - server_name admin.myrafiki.com; + server_name ilp.myrafiki.com; listen 80; - if ($host = admin.myrafiki.com) { - return 301 https://$host$request_uri; + if ($host = ilp.myrafiki.com) { + return 301 https://$host$request_uri; } return 404; } ``` - -## Set up symbolic links - -Once the Nginx configuration files have been created, set up symbolic links that will allow Nginx to read those files and redirect the local paths to the exposed domains and ports. - -```sh -sudo ln -s /etc/nginx/sites-available/admin.conf /etc/nginx/sites-enabled/admin.conf - -sudo ln -s /etc/nginx/sites-available/open_payments_auth_server.conf /etc/nginx/sites-enabled/auth.conf - -sudo ln -s /etc/nginx/sites-available/ilp.conf /etc/nginx/sites-enabled/ilp.conf - -sudo ln -s /etc/nginx/sites-available/open_payments_resource_server.conf /etc/nginx/sites-enabled/oprs.conf - -``` - -## Deploy with docker compose - -Deploy the configured Rafiki services with docker compose: - -```sh -docker compose up -d -``` diff --git a/packages/documentation/src/content/docs/integration/requirements/exchange-rates.mdx b/packages/documentation/src/content/docs/integration/requirements/exchange-rates.mdx index 5f5e06f150..702448c80c 100644 --- a/packages/documentation/src/content/docs/integration/requirements/exchange-rates.mdx +++ b/packages/documentation/src/content/docs/integration/requirements/exchange-rates.mdx @@ -48,12 +48,16 @@ GET https://cloud-nine-wallet/rates ### Response objects +
+ | Variable | Type | Description | Required | | -------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------- | -------- | | `base` | String | The asset code represented as an ISO 4217 currency code, e.g. USD | Y | | `rates` | Object | Object containing `` pairs, e.g. `{EUR: 0.8930}` | Y | | `rates.` | Number | The exchange rate given `base` and `` | Y | +
+ ## Specify rate caching duration (optional) Specify how long your Rafiki instance will cache exchange rates via the `backend` service's `EXCHANGE_RATES_LIFETIME` variable or use the default setting of `15_000` ms (15 seconds). @@ -98,8 +102,12 @@ export function loader({ request }: LoaderFunctionArgs) { ## Environment variables +
+ | Variable | Type | Description | Required | | ------------------------- | --------- | ----------------------------------------------------------------------------------------------- | -------- | | `EXCHANGE_RATES_URL` | `backend` | Your exchange rates endpoint | Y | | `EXCHANGE_RATES_LIFETIME` | `backend` | The amount of time Rafiki caches exchange rates, in ms | Y | | `SLIPPAGE` | `backend` | The variance allowed between a quote and the actual amount required when a payment is initiated | Y | + +
diff --git a/packages/documentation/src/content/docs/integration/requirements/idp.mdx b/packages/documentation/src/content/docs/integration/requirements/idp.mdx index f9c6676e67..4c839264d7 100644 --- a/packages/documentation/src/content/docs/integration/requirements/idp.mdx +++ b/packages/documentation/src/content/docs/integration/requirements/idp.mdx @@ -2,12 +2,17 @@ title: Identity provider (IdP) --- -import { Badge } from '@astrojs/starlight/components' -import { Mermaid, CodeBlock, LinkOut } from '@interledger/docs-design-system' +import { Badge, Steps } from '@astrojs/starlight/components' +import { + Mermaid, + MermaidWrapper, + LinkOut, + CodeBlock +} from '@interledger/docs-design-system' 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 Open Payments outgoing payments for your users. Open Payments requires that outgoing payment grant requests, which precede outgoing payment requests, be interactive. In an interactive grant request, explicit interaction by an individual (e.g., an account holder) is required to approve the grant. An example of an interaction is an end-user tapping an Approve button in an app to authorize a payment. +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. Your IdP will: @@ -15,25 +20,33 @@ Your IdP will: - Facilitate interactions with the client's end-user to gather consent :::note -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 here as your IdP. +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. ::: ### Interactions and consent -Before an Open Payments outgoing payment is created, an outgoing payment grant must be issued. +Before an outgoing payment is created via Open Payments, an outgoing payment _grant_ must be issued. -Outgoing payment grant requests must be interactive. This means the request requires explicit interaction, often from the account holder, to gather consent (permission) to create the outgoing payment. The interaction is facilitated by your IdP. Your IdP: +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. -1. Provides an interface to gather consent, for example, a consent screen -2. Sends the interaction choice to your authorization server +Your IdP: + + + +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 + + ### Authorization server -The purpose of an Open Payments authorization server is to grant permission to clients to access the Open Payments APIs for creating incoming payments, quotes, and outgoing payments against an account holder's account. +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. -Rafiki's [auth service](/integration/services/auth-service) provides you with a reference implementation of an Open Payments authorization server. The server extends an [API](#interaction-endpoints) for your IdP to use to begin and finish an interaction, collect authorization, get information about a particular grant, and communicate that a user has authorized a grant. 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. You can use the service as an alternative to developing your own in-house service. + +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. ## Environment variables @@ -53,19 +66,32 @@ The following `backend` variables must be configured on your authorization serve -## Interaction endpoints +## Manage grants -Your Open Payments authorization server extends an API for your IdP server to use after a pending grant request is created. +After a pending grant request is created, your IdP server can use the interaction endpoints listed below to: -Each interaction with an endpoint is identified by an `id` and a `nonce`. Both are provided as query parameters when your authorization server redirects to your IdP server. +- Start and finish interactions +- Collect authorization +- Get information about a grant +- Communicate whether an end-user has authorized a grant -The endpoints are tied to the `IDENTITY_SERVER_URL` you defined when configuring your environment variables. For example, if your identity server URL is `https://idp.wallet.example.com`, then to start a user interaction session, the `/interact/{id}/{nonce}` endpoint would be called: +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 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://idp.wallet.example.com/interact/{id}/{nonce}` -``` -The endpoints are called in the sequence listed in the table below. + + +### Interaction endpoints + +The endpoints are called in the sequence listed below. + +
| Method | Endpoint | Purpose | | ---------------------------------------------------- | ------------------------------- | ----------------------------------------------------------------- | @@ -75,29 +101,35 @@ The endpoints are called in the sequence listed in the table below. | | `/interact/{id}/{nonce}/finish` | [Finish user interaction](#finish-interaction)
| | | `/interact/{id}/{nonce}` | [Continue grant](#continue-grant) | +
+ We also provide an OpenAPI specification that describes the endpoints. #### Start user interaction session -Called by the client and establishes an interactive session with your authorization server, which redirects the client's browser session to your IdP consent screen. +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. #### Look up grant information -Called by your IdP server to retrieve a list of access rights, requested by the client, from your 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 defined [`INTERACTION_PORT`](#environment-variables). +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). #### Accept or reject grant -Your IdP server communicates the choice made by the end-user on the consent screen (accept/reject) to your authorization server. The request is secured with an [`x-idp-secret`](#x-idp-secret-header) header. Then, your IdP server redirects to the `finish` endpoint. The response is served on your defined [`INTERACTION_PORT`](#environment-variables). +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) #### Finish interaction -Your IdP server ends the interaction and redirects the end-user's browser session to the URI of the grant initialization request. +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. In case of success, the SHA-256 hash of the interaction is sent in the response along with an `interact_ref` that identifies the interaction on the authorization server and 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. -The following table lists examples of the possible response types on this endpoint. +The following are examples of the possible response types. -
+
| Response | Description | Example | | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | @@ -109,16 +141,50 @@ The following table lists examples of the possible response types on this endpoi #### Continue grant -The client requests a grant from your authorization server for an accepted interaction. Your authorization server responds with an access token. +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. -## x-idp-secret header +### x-idp-secret header -`x-idp-secret` is the name of a header that is used for requests to the following endpoints: +The purpose of the `x-idp-secret` header is to secure communications between your IdP and authorization servers. + +The header is used for requests to the following endpoints and its value should be a shared secret known to both entities. - `GET /grant/:id/:nonce` - `POST /grant/:id/:nonce/accept` - `POST /grant/:id/:nonce/reject` -The header's purpose is to secure communications between your IdP and authorization servers and its value should be a shared secret known to both entities. When your IdP server sends requests to your authorization server, your IdP must provide the secret via this header. +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 +`} +/> + + diff --git a/packages/documentation/src/content/docs/integration/requirements/sending-fees.mdx b/packages/documentation/src/content/docs/integration/requirements/sending-fees.mdx index a37bf8548e..ba6975ba6a 100644 --- a/packages/documentation/src/content/docs/integration/requirements/sending-fees.mdx +++ b/packages/documentation/src/content/docs/integration/requirements/sending-fees.mdx @@ -52,6 +52,8 @@ Let's assume your asset scale is 2. You'll charge a fixed fee of 100 (\$1.00) an +
+ | Variable | Description | | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `assetId` | The asset's unique ID assigned by Rafiki when the asset was created. | @@ -59,6 +61,8 @@ Let's assume your asset scale is 2. You'll charge a fixed fee of 100 (\$1.00) an | `fixed` | The amount of the flat, fixed fee to charge. Assuming USD with an asset scale of 2 in the example above, the value of `100` equals \$1.00. | | `basisPoints` | The amount of the variable fee to charge based on the total amount. One basis point is equal to 0.01% of the total amount. `100` basis points equals 1%, and `10000` basis points equals 100%. In the example above, the fee is 1%. | +
+ ```json 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 69e8e37340..47cd4b7cac 100644 --- a/packages/documentation/src/content/docs/integration/requirements/wallet-addresses.mdx +++ b/packages/documentation/src/content/docs/integration/requirements/wallet-addresses.mdx @@ -82,6 +82,8 @@ 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 | @@ -89,6 +91,8 @@ We strongly recommend you store at least the `walletAddress.id` in your internal | `url` | The wallet address's case-insensitive URL | | `additionalProperties` | Optional [additional properties](/apis/graphql/backend/inputobjects/#additionalpropertyinput) associated with the wallet address | +
+ ```json @@ -188,12 +192,16 @@ The request is a standard request to create a JSON Web Key (JWK), which is a JSO Open Payments requires the following values. +
+ | 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 | +
+ Additionally, the request must contain the `walletAddressId` of the wallet address that the key pair will be associated with. 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 d3169eb166..6140fc887e 100644 --- a/packages/documentation/src/content/docs/integration/requirements/webhook-events.mdx +++ b/packages/documentation/src/content/docs/integration/requirements/webhook-events.mdx @@ -26,20 +26,28 @@ For Rafiki to notify you about webhook events, you must expose a webhook endpoin When an event occurs, the [`backend`](/integration/services/backend-service) service makes a request to your configured webhook endpoint. The `backend` service expects a `200` status in return. +
+ | Variable | Type | Description | | ------------- | --------- | ------------------------------------------------------------------- | | `WEBHOOK_URL` | `backend` | The endpoint to where requests are made when a webhook event occurs | +
+ ## Webhook event request body Each webhook event is sent as a JSON payload with the following structure in the request body. The parameters within the `data` object will vary depending on the event. +
+ | Attribute | Type | Description | Required | | --------- | ------ | --------------------------------------------------- | -------- | | `id` | String | UUID for the event | Y | | `type` | Enum | The `EventType` | Y | | `data` | Object | Additional data that coincides with the `EventType` | Y | +
+ :::tip[Duplicate events] 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. ::: @@ -143,38 +151,30 @@ If a non-200 status is returned, indicating an error, or the request times out, The first retry is after 10 seconds. Additional retries occur after 20 more seconds, then after 30 more seconds, and so on. +
+ | Variable | Type | Description | | ------------------- | --------- | --------------------------------------------------------------------------------------------------------------- | | `WEBHOOK_TIMEOUT` | `backend` | The amount of time, in milliseconds, after which a webhook request will time out | | `WEBHOOK_MAX_RETRY` | `backend` | The maximum number of retries for a webhook event when a non-200 status is returned or if the request timed out | +
+ ## Webhook events ### Incoming payments +
+ | Event type | Description | | ----------------------------------------------------------- | --------------------------------------------------------------------------------- | | [`incoming_payment.created`](#incoming-payment-created) | An incoming payment has been created | | [`incoming_payment.completed`](#incoming-payment-completed) | An incoming payment is complete and will not accept any additional incoming funds | | [`incoming_payment.expired`](#incoming-payment-expired) | An incoming payment expired and will not accept any additional incoming funds | -#### Incoming payment created - -
-Expand for event sequence - ->ASE: Fires incoming_payment.created event to webhook endpoint - ASE->>ASE: No action required - -`} -/> +
- +#### Incoming payment created + | Event type | Description | | ----------------------------------------------------------- | -------------------------------------------------- | | [`outgoing_payment.created`](#outgoing-payment-created) | An outgoing payment has been created | | [`outgoing_payment.completed`](#outgoing-payment-completed) | An outgoing payment has completed | | [`outgoing_payment.failed`](#outgoing-payment-failed) | An outgoing payment partially or completely failed | +
+ #### Outgoing payment created @@ -369,11 +373,15 @@ The `outgoing_payment.failed` event indicates that an outgoing payment has eithe ### Wallet addresses +
+ | Event type | Description | | --------------------------------------------------------------------- | ------------------------------------------------------------------ | | [`wallet_address.not_found`](#wallet-address-not-found) | The requested wallet address was not found on this Rafiki instance | | [`wallet_address.web_monetization`](#wallet-address-web-monetization) | Web Monetization payments have been received via STREAM | +
+ #### Wallet address not found @@ -397,10 +405,14 @@ The `wallet_address.not_found` event indicates that a wallet address was request When you receive this event, look up the associated account in your system and create a wallet address for the account. The initial wallet address request will succeed if you create it within your configured `WALLET_ADDRESS_LOOKUP_TIMEOUT_MS` time frame. +
+ | Environment variable | Type | Description | | ---------------------------------- | --------- | -------------------------------------------------------------------------------------------------------------- | | `WALLET_ADDRESS_LOOKUP_TIMEOUT_MS` | `backend` | The time in milliseconds that you have to create a missing wallet address before the initial request times out | +
+ #### Wallet address Web Monetization @@ -425,10 +437,14 @@ The `wallet_address.web_monetization` event indicates that a wallet address rece ### Low asset liquidity +
+ | Event type | Description | | --------------------------------------------- | ------------------------------------------------------------- | | [`asset.liquidity_low`](#asset-liquidity-low) | Your asset liquidity has dropped below your defined threshold | +
+ #### Asset liquidity low @@ -452,10 +468,14 @@ The `asset.liquidity_low` event indicates that an asset's liquidity has dropped ### Low peer liquidity +
+ | Event type | Description | | ------------------------------------------- | ------------------------------------------------------------ | | [`peer.liquidity_low`](#peer-liquidity-low) | Your peer liquidity has dropped below your defined threshold | +
+ #### Peer liquidity low diff --git a/packages/documentation/src/content/docs/integration/services/auth-service.mdx b/packages/documentation/src/content/docs/integration/services/auth-service.mdx index dad5fcc686..a262c4cd66 100644 --- a/packages/documentation/src/content/docs/integration/services/auth-service.mdx +++ b/packages/documentation/src/content/docs/integration/services/auth-service.mdx @@ -32,17 +32,19 @@ When a request comes from a client with an account known to your local instance When a request comes from a client registered with another instance of Rafiki, the `auth` service resolves the client's key endpoint (e.g., `https://wallet.example.com/alice/jwks.json`) to retrieve the client's public keys, then filters out the correct key using the key id (`kid`) in the client's signature. +Review the Open Payments documentation for more information about client keys. + ## Identity provider (IdP) An identity provider (IdP) is a system or service that manages user authentication, identity information, and consent. When you use your Google account credentials to “Sign in with Google” on an app or website, for example, Google is acting as your identity provider. -Integration with an [IdP](/integration/requirements/idp) is required when using Rafiki’s `auth` service because Open Payments requires interactive outgoing payment grant requests. This means there must be explicit interaction by an individual (typically a client’s end user/your customer) to approve or deny an outgoing payment before a grant is issued. +Integration with an [IdP](/integration/requirements/idp) is required when using Rafiki’s `auth` service because the Open Payments standard requires interactive outgoing payment _grant_ requests. In an interactive request, there must be explicit interaction by an individual (e.g., a client's end-user) to approve or deny the grant. In this case, the grant must be explicitly approved before an outgoing payment is created. :::note -Rafiki’s [`frontend`](/integration/services/frontend-service) service requires an IdP for authentication and user management of your administrators. Out of the box, Rafiki uses Ory Kratos to enable your admins to access the Admin API. Kratos is for internal use and cannot be used for your customer-facing Open Payments authorization server. +Rafiki’s [`frontend`](/integration/services/frontend-service) service requires an IdP for authentication and user management of your [Rafiki Admin](/admin/admin-user-guide) users. Out of the box, Rafiki uses Ory Kratos, a cloud-based user management system. Kratos is for internal use only and **cannot** be used as your customer-facing Open Payments authorization server. ::: -For more information about interactive grants and how they work with identity providers, review the Grant negotiation and authorization page in the Open Payments docs. +For more information about interactive grants and how they work with identity providers, review our [Identity Provider](/integration/requirements/idp) page and the Grant negotiation and authorization page in the Open Payments docs. ## GraphQL Auth Admin API diff --git a/packages/documentation/src/content/docs/overview/concepts/accounting.mdx b/packages/documentation/src/content/docs/overview/concepts/accounting.mdx index 3c74bed597..c7430a16a7 100644 --- a/packages/documentation/src/content/docs/overview/concepts/accounting.mdx +++ b/packages/documentation/src/content/docs/overview/concepts/accounting.mdx @@ -38,12 +38,16 @@ An asset represents an item of value that can be transferred via the Interledger In Rafiki, the `asset` type is comprised of the following properties. +
+ | 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` | +
+ To convert an asset amount into a currency amount that's easier to read, apply the following formula: $currencyAmount = \frac{value}{10^{assetScale}}$ diff --git a/packages/documentation/src/content/docs/overview/concepts/open-payments.mdx b/packages/documentation/src/content/docs/overview/concepts/open-payments.mdx index b6f53ecce9..ca7c364e7c 100644 --- a/packages/documentation/src/content/docs/overview/concepts/open-payments.mdx +++ b/packages/documentation/src/content/docs/overview/concepts/open-payments.mdx @@ -35,4 +35,4 @@ Rafiki’s [`backend`](/integration/services/backend-service) service is the mai ## Rafiki's auth service -Rafiki’s [`auth`](/integration/services/auth-service) service is a reference implementation of an opinionated Open Payments authorization server. The authorization server is responsible for delegating authorization (via grants) to clients to use the Open Payments APIs, resolving clients’ public keys to authenticate and authorize incoming requests, and creating payments and quotes on the backend. Open Payments leverages the Grant Negotiation and Authorization Protocol (GNAP) for delegating authorization. You can learn more about the protocol by reviewing its specification. +Rafiki’s [`auth`](/integration/services/auth-service) service is a reference implementation of an opinionated Open Payments authorization server. The authorization server is responsible for delegating authorization (via grants) to clients to use the Open Payments APIs, resolving clients’ public keys to authenticate and authorize incoming requests, and creating payments and quotes on the backend. Open Payments leverages the Grant Negotiation and Authorization Protocol (GNAP) for delegating authorization. You can learn more about the protocol by reviewing its specification. diff --git a/packages/documentation/src/content/docs/overview/concepts/telemetry.mdx b/packages/documentation/src/content/docs/overview/concepts/telemetry.mdx index d9e7da79b8..1386ce6af7 100644 --- a/packages/documentation/src/content/docs/overview/concepts/telemetry.mdx +++ b/packages/documentation/src/content/docs/overview/concepts/telemetry.mdx @@ -161,7 +161,7 @@ You must deploy your own OTEL Collector that acts as a sidecar container to Rafi ### Telemetry environment variables -
+
| Variable name | Type | Description | Required | | -------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | diff --git a/packages/documentation/src/content/docs/resources/webhook-event-types.mdx b/packages/documentation/src/content/docs/resources/webhook-event-types.mdx index 4291ef7fa9..0bad323af4 100644 --- a/packages/documentation/src/content/docs/resources/webhook-event-types.mdx +++ b/packages/documentation/src/content/docs/resources/webhook-event-types.mdx @@ -6,6 +6,8 @@ Webhooks notify you of specific events that occur within your Rafiki instance al The following is an enumeration of all of Rafiki's [webhook event](/integration/requirements/webhook-events) types along with their descriptions, which you must listen to and handle. +
+ | Value | Description | | -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | | [`incoming_payment.created`](/integration/requirements/webhook-events/#incoming-payment-created) | An incoming payment has been created. | @@ -18,3 +20,5 @@ The following is an enumeration of all of Rafiki's [webhook event](/integration/ | [`wallet_address.web_monetization`](/integration/requirements/webhook-events/#wallet-address-web-monetization) | Web Monetization payments received via STREAM. | | [`asset.liquidity_low`](/integration/requirements/webhook-events/#asset-liquidity-low) | Asset liquidity has dropped below defined threshold. | | [`peer.liquidity_low`](/integration/requirements/webhook-events/#peer-liquidity-low) | Peer liquidity has dropped below defined threshold. | + +
diff --git a/packages/documentation/src/partials/backend-variables.mdx b/packages/documentation/src/partials/backend-variables.mdx index 5f4d4d41ae..08ae12bf44 100644 --- a/packages/documentation/src/partials/backend-variables.mdx +++ b/packages/documentation/src/partials/backend-variables.mdx @@ -24,6 +24,7 @@ import { LinkOut } from '@interledger/docs-design-system' | `INSTANCE_NAME` | | `undefined` | this Rafiki instance's name used to communicate for [auto-peering](/integration/playground/autopeering/) | | `KEY_ID` | backend.key.id | `undefined` | this Rafiki instance's client key id | | `LOG_LEVEL` | backend.logLevel | `info` | [Pino Log Level](https://getpino.io/#/docs/api?id=levels) | +| `MAX_OUTGOING_PAYMENT_RETRY_ATTEMPTS` | | `5` | Maximum number of retry attempts for an outgoing payment before it is considered failed | | `NODE_ENV` | backend.nodeEnv | `development` | node environment, `development`, `test`, or `production` | | `OPEN_PAYMENTS_PORT` | backend.port.openPayments | `3003` | port of the Open Payments resource server port | | `OPEN_PAYMENTS_URL` | backend.serviceUrls.OPEN_PAYMENTS_URL | `undefined` | public endpoint of this Open Payments Resource Server | diff --git a/packages/documentation/src/styles/rafiki.css b/packages/documentation/src/styles/rafiki.css index 10cff423be..d648e2bd40 100644 --- a/packages/documentation/src/styles/rafiki.css +++ b/packages/documentation/src/styles/rafiki.css @@ -98,6 +98,8 @@ 0 0, 100%; background-attachment: local, local, scroll, scroll; + box-shadow: var(--sl-shadow-sm); + border-radius: var(--border-radius); } .overflow-table thead th {