@interledger/pay
TypeScript icon, indicating that this package has built-in type declarations

0.4.0-alpha.9 • Public • Published

pay 💸

Send payments over Interledger using STREAM

NPM Package GitHub Actions Codecov Prettier

Install

npm i @interledger/pay

Or using Yarn:

yarn add @interledger/pay

Guide

Flow

  1. Call setupPayment to resolve the payment details, destination asset, and/or Incoming Payment
  2. Add custom logic before continuing, or catch error
  3. Call startQuote to probe the exchange rate, discover the max packet amount, and compute payment limits
  4. Add custom logic to authorize payment for maximum source amount, or catch error
  5. Call pay to execute the payment
  6. Add custom logic to handle payment outcome or error

Pay an Incoming Payment

Fixed delivery amount payment

import { setupPayment, startQuote, pay, closeConnection } from '@interledger/pay'

async function run() {
  let plugin /* Plugin instance */

  const destination = await setupPayment({
    plugin,
    destinationPayment:
      'https://mywallet.example/accounts/alice/incoming-payments/04ef492f-94af-488e-8808-3ea95685c992',
  })

  const quote = await startQuote({
    plugin,
    destination,
    sourceAsset: {
      assetCode: 'USD',
      assetScale: 9,
    },
  })
  // {
  //   maxSourceAmount: 1_950n,
  //   lowEstimatedExchangeRate: 115,
  //   highEstimatedExchangeRate: 135,
  //   minExchangeRate: 110,
  // }

  // Verify the max source amount is appropriate and perform or cancel the payment
  const receipt = await pay({ plugin, destination, quote })
  console.log(receipt)
  // {
  //    amountSent: 1_910n,
  //    amountDelivered: BigInt(234_000),
  //    ...
  // }

  await closeConnection(plugin, destination)
}

Pay an Incoming Payment via a Connection URL

Fixed delivery amount payment

import { setupPayment, startQuote, pay, closeConnection } from '@interledger/pay'

async function run() {
  let plugin /* Plugin instance */

  const destination = await setupPayment({
    plugin,
    destinationConnection: 'https://mywallet.example/bddcc820-c8a1-4a15-b768-95ea2a4ed37b',
  })

  const quote = await startQuote({
    plugin,
    destination,
    sourceAsset: {
      assetCode: 'USD',
      assetScale: 9,
    },
  })
  // {
  //   maxSourceAmount: 1_950n,
  //   lowEstimatedExchangeRate: 115,
  //   highEstimatedExchangeRate: 135,
  //   minExchangeRate: 110,
  // }

  // Verify the max source amount is appropriate and perform or cancel the payment
  const receipt = await pay({ plugin, destination, quote })

  await closeConnection(plugin, destination)
}

Pay to a Payment Pointer

Fixed source amount payment

import { setupPayment, startQuote, pay, closeConnection } from '@interledger/pay'

async function run() {
  let plugin /* Plugin instance */

  const destination = await setupPayment({
    plugin,
    paymentPointer: '$rafiki.money/p/example',
  })

  const quote = await startQuote(
    plugin,
    amountToSend: '314159',
    sourceAmount: {
      assetCode: 'EUR',
      assetScale: 6,
    },
    destination
  })

  const receipt = await pay({ plugin, destination, quote })

  await closeConnection(plugin, destination)
}

Units

On Interledger assets and denominations:

Asset amounts may be represented using any arbitrary denomination. For example, one U.S. dollar may be represented as $1 or 100 cents, each of which is equivalent in value. Likewise, one Bitcoin may be represented as 1 BTC or 100,000,000 satoshis.

A standard unit is the typical unit of value for a particular asset, such as $1 in the case of U.S. dollars, or 1 BTC in the case of Bitcoin.

A fractional unit represents some unit smaller than the standard unit, but with greater precision. Examples of fractional monetary units include one cent ($0.01 USD), or 1 satoshi (0.00000001 BTC).

An asset scale is the difference in orders of magnitude between the standard unit and a corresponding fractional unit. More formally, the asset scale is a non-negative integer (0, 1, 2, …) such that one standard unit equals the value of 10^(scale) corresponding fractional units. If the fractional unit equals the standard unit, then the asset scale is 0.

For example, one cent represents an asset scale of 2 in the case of USD, whereas one satoshi represents an asset scale of 8 in the case of Bitcoin.

To simplify accounting, all amounts are represented as unsigned integers in a fractional unit of the asset corresponding to the source asset scale provided, or the destination asset scale resolved from the receiver.

Since applications need to debit the source amount in their own system before executing a payment, this assumes they also know their own source asset and denomination. Therefore, it's not useful to resolve this information dynamically, such as using IL-DCP, which also delays connection establishment.

Amounts

Pay leverages JavaScript BigInt for arbitrarily large integers using its own wrapper for strongly-typed arithmetic operations.

Amounts returned by Pay use these exported classes and interfaces:

  • Int — Class representing non-negative integers.
  • PositiveInt — Interface narrowing Int, representing non-negative, non-zero integers. (In this context, zero is not considered signed).
  • Ratio — Class representing a ratio of two integers: a non-negative numerator, and a non-negative, non-zero denominator.
  • PositiveRatio — Interface narrowing Ratio, representing a ratio of two non-negative, non-zero integers.

Int and Ratio offer utility methods for integer operations and comparisons. They may also be converted to/from number, string, bigint, and Long.

Int and Ratio prevent divide-by-zero errors and enforce the internal bigint is always non-negative. They also provide type guards for PositiveInt to reduce unnecessary code paths. For example, if one integer is greater than another, that integer must always be non-zero, and can be safely used as a ratio denominator without any divide-by-zero branch.

Exchange Rates

Pay is designed to provide strict guarantees of the amount that will be delivered.

During the quote step, the application provides Pay with prices for the source and destination assets and its own acceptable slippage percentage, which Pay uses to calculate a minimum exchange rate and corresponding minimum destination amount it will enforce for the payment. Exchange rates are represented as the ratio between a destination amount and a source amount, in fractional units.

Then, Pay probes the recipient to determine the real exchange rate over that path. If it sufficiently exceeds the minimum exchange rate, Pay will allow the payment to proceed. Otherwise, it's not possible to complete the payment. For instance, connectors may have applied a poor rate or charged too much in fees, the max packet size might be too small to avoid rounding errors, or incorrect assets/scales were provided.

Since STREAM payments are packetized, Pay may not be able to complete a payment if, for instance, the sender and receiver become disconnected during the payment. However, Pay guarantees payments never exhaust their quoted maximum source amount without satisfying their quoted minimum delivery amount. Every delivered packet meets or exceeds the quoted minimum exchange rate (*with the exception of the final one, as necessary).

Error Handling

If setup or quoting fails, Pay will reject the Promise with a variant of the PaymentError enum. For example:

import { setupPayment, PaymentError } from '@interledger/pay'

try {
  await setupPayment({ ... })
} catch (err) {
  if (err === PaymentError.InvalidPaymentPointer) {
    console.log('Payment pointer is invalid!')
  }

  // ...
}

Similarly, if an error was encountered during the payment itself, it will include an error property on the result which is a PaymentError variant.

A predicate function, isPaymentError, is also exported to check if any value is a variant of the enum.

Payment Pointers

Pay exports the AccountUrl utility to validate payment pointers and SPSP/Open Payments account URLs. Since payment pointers identify unique Interledger accounts, Pay parses them so they can be compared against external references to the same account.

Connection Security

Some applications may find it useful for multiple Pay library instances to send over a single STREAM connection, such as quoting in one process, and sending money in another.

In this case, the client application must track key security parameters, such as the request count, which STREAM relies on for monotonically increasing sequence numbers and secure acknowledgements of each request.

Pay uses a Counter instance, passed in-process via the ResolvedPayment object, to track how many packets have been sent. Applications that resume connections MUST use the counter instance to fetch how many packets have been sent, then create a new counter with the existing request count to pass to new Pay instances that use the same connection.

Other connection invariants applications should enforce:

  1. Only one Pay instance (any actively running call to startQuote, pay, or closeConnection) can send over a single connection at one time.
  2. After a connection is closed via calling closeConnection, those connection details may no longer be used for sending.

API

setupPayment

(options:SetupOptions) => Promise<ResolvedPayment>

Resolve destination details and asset of the payment in order to establish a STREAM connection.

startQuote

(options:QuoteOptions) => Promise<Quote>

Perform a rate probe: discover path max packet amount, probe the real exchange rate, and compute the minimum exchange rate and bounds of the payment.

pay

(options:PayOptions) => Promise<PaymentProgress>

Send the payment: send a series of packets to attempt the payment within the completion criteria and limits of the provided quote.

closeConnection

(plugin:Plugin, destination:ResolvedPayment) => Promise<void>

If the connection was established, notify receiver to close the connection. For stateless receivers, this may have no effect.

SetupOptions

Interface

Parameters to setup and resolve payment details from the recipient.

Property Type Description
plugin Plugin Plugin to send packets over a connected Interledger network (no receive functionality is necessary). Pay does not call connect or disconnect on the plugin, so the application must perform that manually.
destinationAccount (Optional) string SPSP Payment pointer or SPSP account URL to query STREAM connection credentials and exchange asset details. Example: $rafiki.money/p/alice. Either destinationAccount , destinationPayment, or destinationConnection must be provided.
destinationPayment (Optional) string Open Payments Incoming Payment URL to query the details for a fixed-delivery payment. The amount to deliver and destination asset details will automatically be resolved from the Incoming Payment. Either destinationAccount , destinationPayment, or destinationConnection must be provided.
destinationConnection (Optional) string Open Payments STREAM Connection URL to query STREAM connection credentials and exchange asset details for a fixed-delivery payment. Either destinationAccount , destinationPayment, or destinationConnection must be provided.
amountToDeliver (Optional) Amount Fixed amount of the created Incoming Payment, in base units of the destination asset.

Note: this option requires the destination asset to be known in advance. The application must ensure the destination asset resolved via STREAM is the expected asset and denomination.

ResolvedPayment

Interface

Resolved destination details of a proposed payment, such as the destination asset, Incoming Payment, and STREAM credentials, ready to perform a quote.

Property Type Description
destinationAsset AssetDetails Destination asset and denomination, resolved using Open Payments or STREAM, or provided directly.
destinationAddress string ILP address of the destination STREAM recipient, uniquely identifying this connection.
sharedSecret Uint8Array 32-byte seed to derive keys to encrypt STREAM messages and generate ILP packet fulfillments.
destinationPaymentDetails (Optional) IncomingPayment Open Payments Incoming Payment metadata, if the payment pays into an Incoming Payment.
accountUrl (Optional) string URL of the recipient Open Payments/SPSP account (with well-known path, and stripped trailing slash). Each payment pointer and its corresponding account URL identifies a unique payment recipient. Not applicable if Open Payments STREAM Connection URL or STREAM credentials were provided directly.
destinationAccount (Optional) string Payment pointer, prefixed with "$", corresponding to the recipient Open Payments/SPSP account. Each payment pointer and its corresponding account URL identifies a unique payment recipient. Not applicable if STREAM credentials were provided directly.
requestCounter Counter Strict counter of how many packets have been sent, to safely resume a connection

QuoteOptions

Interface

Limits and target to quote a payment and probe the rate.

Property Type Description
plugin Plugin Plugin to send packets over a connected Interledger network (no receive functionality is necessary). Pay does not call connect or disconnect on the plugin, so the application must perform that manually.
destination ResolvedPayment Resolved destination details of the payment, including the asset, Incoming Payment, and connection establishment information.
sourceAsset (Optional) AssetDetails Source asset and denomination for the sender. Required to compute the minimum exchange rate, unless slippage is 100%.
amountToSend (Optional) string, number, bigint or Int Fixed amount to send to the recipient, in base units of the sending asset. Either amountToSend, amountToDeliver, or destinationPayment must be provided, in order to determine how much to pay.
amountToDeliver (Optional) string, number, bigint or Int Fixed amount to deliver to the recipient, in base units of the destination asset. destinationPayment is recommended method to send fixed delivery payments, but this option enables sending a fixed-delivery payment to an SPSP server that doesn't support Open Payments.

Note: this option requires the destination asset to be known in advance. The application must ensure the destination asset resolved via STREAM is the expected asset and denomination.
prices (Optional) { [string]: number } Object of asset codes to prices in a standardized base asset to compute exchange rates. For example, using U.S. dollars as a base asset: { USD: 1, EUR: 1.09, BTC: 8806.94 }.

If the source and destination assets are the same, a 1:1 rate will be used as the basis, so prices doesn't need to be provided. It may also be omitted if the slippage is set to 100%, since no minimum exchange rates will be enforced.
slippage (Optional) number Percentage to subtract from the external exchange rate to determine the minimum acceptable exchange rate and destination amount for each packet, between 0 and 1 (inclusive). Defaults to 0.01, or 1% slippage below the exchange rate computed from the given prices.

If 1 is provided for a fixed source amount payment, no minimum exchange rate will be enforced. For fixed delivery payments, slippage cannot be 100%.

Quote

Interface

Parameters of payment execution and the projected outcome of a payment.

Property Type Description
paymentType PaymentType The completion criteria of the payment. For fixed source amount payments, "FixedSend"; for Incoming Payments and fixed delivery payments, "FixedDelivery".
maxSourceAmount bigint Maximum amount that will be sent in the base unit and asset of the sending account. This is intended to be presented to the user or agent before authorizing a fixed delivery payment. For fixed source amount payments, this will be the provided amountToSend.
minDeliveryAmount bigint Minimum amount that will be delivered if the payment completes, in the base unit and asset of the receiving account. For fixed delivery payments, this will be the provided amountToDeliver or amount of the Incoming Payment.
maxPacketAmount bigint Discovered maximum packet amount allowed over this payment path.
minExchangeRate Ratio Aggregate exchange rate the payment is guaranteed to meet, as a ratio of destination base units to source base units. Corresponds to the minimum exchange rate enforced on each packet (*except for the final packet) to ensure sufficient money gets delivered. For strict bookkeeping, use maxSourceAmount instead.
lowEstimatedExchangeRate Ratio Lower bound of probed exchange rate over the path (inclusive). Ratio of destination base units to source base units
highEstimatedExchangeRate Ratio Upper bound of probed exchange rate over the path (exclusive). Ratio of destination base units to source base units

PayOptions

Interface

Payment execution parameters.

Property Type Description
plugin Plugin Plugin to send packets over a connected Interledger network (no receive functionality is necessary). Pay does not call connect or disconnect on the plugin, so the application must perform that manually.
destination ResolvedPayment Resolved destination details of the payment, including the asset, Incoming Payment, and connection establishment information.
quote Quote Parameters and rates to enforce during payment execution.
progressHandler (Optional) (progress:PaymentProgress) => void Callback to process streaming updates as packets are sent and received, such as to perform accounting while the payment is in progress. Handler will be called for all fulfillable packets and replies before the payment resolves.

PaymentProgress

Interface

Intermediate state or outcome of the payment, to account for sent/delivered amounts. If the payment failed, the error property is included.

Property Type Description
error (Optional) PaymentError Error state, if the payment failed.
amountSent bigint Amount sent and fulfilled, in base units of the source asset.
amountDelivered bigint Amount delivered to the recipient, in base units of the destination asset.
sourceAmountInFlight bigint Amount sent that is yet to be fulfilled or rejected, in base units of the source asset.
destinationAmountInFlight bigint Estimate of the amount that may be delivered from in-flight packets, in base units of the destination asset.
streamReceipt (Optional) Uint8Array Latest STREAM receipt to provide proof-of-delivery to a 3rd party verifier.

IncomingPayment

Interface

Open Payments Incoming Payment metadata

Property Type Description
id string URL used to query and identify the Incoming Payment.
paymentPointer string URL of the recipient Open Payments account to which incoming payments will be credited (with well-known path, and stripped trailing slash). Each payment pointer and its corresponding paymentPointer identifies a unique payment recipient.
completed boolean Describes whether the Incoming Payment has completed receiving funds.
incomingAmount Amount (Optional) Fixed destination amount that must be delivered to complete payment of the Incoming Payment.
receivedAmount Amount Amount that has already been paid toward the Incoming Payment.
expiresAt number (Optional) UNIX timestamp in milliseconds after which payments toward the Incoming Payment will no longer be accepted.
description string (Optional) Human-readable description of what is provided in return for completion of the Incoming Payment.
externalRef string (Optional) Human-readable external reference that can be used by external systems to reconcile this payment with outside systems.

Amount

Interface

Amount details of an IncomingPayment.

Property Type Description
value bigint Amount, in base units.
assetScale number Precision of the asset denomination: number of decimal places of the ordinary unit.
assetCode string Asset code or symbol identifying the currency of the account.

AssetDetails

Interface

Asset and denomination for an Interledger account (source or destination asset)

Property Type Description
scale number Precision of the asset denomination: number of decimal places of the ordinary unit.
code string Asset code or symbol identifying the currency of the account.

PaymentType

String enum

Completion criteria of the payment

Variant Description
FixedSend Send up to a maximum source amount
FixedDelivery Send to meet a minimum delivery amount, bounding the source amount and rates

PaymentError

String enum

Payment error states

Errors likely caused by the user
Variant Description
InvalidPaymentPointer Payment pointer or SPSP URL is syntactically invalid
InvalidCredentials No valid STREAM credentials or URL to fetch them was provided
InvalidSlippage Slippage percentage is not between 0 and 1 (inclusive)
UnknownSourceAsset Source asset or denomination was not provided
UnknownPaymentTarget No fixed source amount or fixed destination amount was provided
InvalidSourceAmount Fixed source amount is not a positive integer
InvalidDestinationAmount Fixed delivery amount is not a positive integer
UnenforceableDelivery Minimum exchange rate of 0 cannot enforce a fixed-delivery payment
Errors likely caused by the receiver, connectors, or other externalities
Variant Description
QueryFailed Failed to query the Open Payments or SPSP server, or received an invalid response
IncomingPaymentCompleted Incoming payment was already completed by the Open Payments server, so no payment is necessary
IncomingPaymentExpired Incoming payment has already expired, so no payment is possible
ConnectorError Cannot send over this path due to an ILP Reject error
EstablishmentFailed No authentic reply from receiver: packets may not have been delivered
UnknownDestinationAsset Destination asset details are unknown or the receiver never provided them
DestinationAssetConflict Receiver sent conflicting destination asset details
ExternalRateUnavailable Failed to compute minimum rate: prices for source or destination assets were invalid or not provided
RateProbeFailed Rate probe failed to establish the exchange rate or discover path max packet amount
InsufficientExchangeRate Real exchange rate is less than minimum exchange rate with slippage
IdleTimeout No packets were fulfilled within timeout
ClosedByReceiver Receiver closed the connection or stream, terminating the payment
IncompatibleReceiveMax Estimated destination amount exceeds the receiver's limit
ReceiverProtocolViolation Receiver violated the STREAM protocol, misrepresenting delivered amounts
MaxSafeEncryptionLimit Encrypted maximum number of packets using the key for this connection

Readme

Keywords

none

Package Sidebar

Install

npm i @interledger/pay

Weekly Downloads

539

Version

0.4.0-alpha.9

License

Apache-2.0

Unpacked Size

340 kB

Total Files

62

Last publish

Collaborators

  • interledger