Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs(0009): upgrade SPSP for PSK2 #367

Merged
merged 8 commits into from Apr 19, 2018
@@ -1,26 +1,26 @@
---
title: The Simple Payment Setup Protocol (SPSP)
draft: 4
draft: 5
---
# Simple Payment Setup Protocol (SPSP)

## Preface

This document describes the Simple Payment Setup Protocol (SPSP), a basic protocol for exchanging payment information between senders and receivers to set up an Interledger payment. SPSP uses the [Pre-Shared Key (PSK)](../0016-pre-shared-key/0016-pre-shared-key.md) transport protocol for condition generation and data encoding.
This document describes the Simple Payment Setup Protocol (SPSP), a basic protocol for exchanging payment information between senders and receivers to facilitate payment over Interledger. SPSP uses the [Pre-Shared Key Version 2 (PSK2)](../0025-pre-shared-key-2/0025-pre-shared-key-2.md) transport protocol for condition generation and data encoding.

## Introduction

### Motivation

The Interledger Protocol does not specify how payment details, such as the ILP Packet or Crypto Condition, should be exchanged between the sender and receiver. SPSP is a minimal protocol that uses HTTPS for communicating these details.
PSK2 does not specify how payment details, such as the ILP address or shared secret, should be exchanged between the sender and receiver. SPSP is a minimal protocol that uses HTTPS for communicating these details.

### Scope

SPSP provides for exchanging basic receiver details needed by a sender to set up an ILP payment. It is intended for use by end-user applications.
SPSP provides for exchanging basic receiver details needed by a sender to set up a PSK2 payment. It is intended for use by end-user applications.

### Interfaces

SPSP may be used by end-user applications, such as a digital wallet with a user interface for the sender to initiate payments. SPSP clients and receivers use ILP modules to send and receive Interledger payments.
SPSP may be used by end-user applications, such as a digital wallet with a user interface for the sender to initiate payments. SPSP clients and receivers use ILP modules to send and receive Interledger payments. SPSP [payment-pointers](../0026-payment-pointers/0026-payment-pointers.md) can be used as a persistent identifier on Interledger. SPSP payment-pointers can also be used as a unique identifier for an invoice to be paid.

SPSP messages MUST be exchanged over HTTPS.

Expand All @@ -33,31 +33,37 @@ Any SPSP receiver will run an SPSP server and expose an HTTPS endpoint called th
* **SPSP Client** - The sender application that uses SPSP to interact with the SPSP Server
* **SPSP Server** - The server used on the receiver's side to handle SPSP requests
* **SPSP Endpoint** - The specific HTTPS endpoint on the SPSP server used for setting up a payment
* **PSK Module** - Software included in the SPSP Client and Server that implements the [Pre-Shared Key](../0016-pre-shared-key/0016-pre-shared-key.md) protocol
* **PSK Module** - Software included in the SPSP Client and Server that implements the [Pre-Shared Key Version 2](../0025-pre-shared-key-2/0025-pre-shared-key-2.md) protocol.

## Overview

### Relation to Other Protocols

SPSP is used for exchanging payment information before an ILP payment is initiated. The sender and receiver use the [Pre-Shared Key (PSK)](../0016-pre-shared-key/0016-pre-shared-key.md) transport protocol to generate the condition and fulfillment. The receiver generates the shared secret to be used in PSK and communicates it to the sender over HTTPS.
SPSP is used for exchanging payment information before an ILP payment is initiated. The sender and receiver use the [Pre-Shared Key Version 2 (PSK2)](../0025-pre-shared-key-2/0025-pre-shared-key-2.md) transport protocol to generate the ILP packets. The receiver generates the shared secret and ILP address to be used in PSK2 and communicates it to the sender over HTTPS.

### Model of Operation

We assume that the sender knows the receiver's SPSP endpoint (see [Appendix A: (Optional) Webfinger Discovery](#appendix-a-optional-webfinger-discovery)).
We assume that the sender knows the receiver's SPSP endpoint (see [Payment Pointers](../0026-payment-pointers/0026-payment-pointers.md)).

1. The sender's SPSP Client queries the receiver's SPSP Endpoint.
2. The SPSP Endpoint responds with the receiver info, including the receiver's ILP address and the shared secret to be used in PSK.
2. The SPSP Endpoint responds with the receiver info, including the receiver's ILP address and the shared secret to be used in PSK2. It MAY respond with a balance associated with this SPSP receiver, i.e. in the case of an invoice.
3. The sender constructs an ILP payment using the receiver's ILP address.
4. The sender uses PSK to generate the payment condition and format additional data intended for the reciever to be sent with the payment.
5. The sender prepares a local transfer to a connector with the condition and ILP packet attached.
6. The receiver's PSK module registers the incoming transfer, parses and validates the ILP packet and the incoming transfer.
7. The receiver MAY submit the incoming payment to an external system for review to ensure that the funds are wanted.
8. If the payment is expected, the receiver's PSK module submits the condition fulfillment to the ledger to execute the transfer. If not, the PSK module rejects the incoming transfer.
9. If the receiver executed the transfer, the sender's SPSP client receives a notification from its ILP module that the transfer has been executed, including the condition fulfillment from the receiver, and notifies the sender that the payment is completed. If the receiver rejected the transfer, the sender's SPSP client receives a notification with the receiver-specified error message detailing why the payment was rejected.
4. The sender begins sending the payment.
1. The sender uses PSK2 to generate the payment chunk and format additional data intended for the reciever to be sent with the payment.
2. The sender sends a prepare packet to a connector with the condition and ILP address produced by PSK2.
3. The receiver's PSK2 module registers the incoming packet, parses, and validates the ILP packet.
4. The receiver MAY submit the incoming packet to an external system for review to ensure that the funds are wanted.
5. If the payment is expected, the receiver's PSK2 module derives a fulfillment packet from the prepare packet. If not, the PSK2 module replies with a reject packet.
6. The receiver MAY update a balance related to this SPSP receiver, i.e. in the case of an invoice.
7. The fulfillment or rejection packet comes back to the sender.
8. The sender MAY send further payment chunks with PSK2 (repeating these steps).

## Specification

The SPSP endpoint is a URI used by the sender to query information about the receiver and set up payments. The SPSP endpoint URI MAY contain query string parameters. The sender SHOULD treat the URI as opaque.
The SPSP endpoint is a URI used by the sender to query information about the receiver (which may be an invoice) and set up payments. The SPSP endpoint URI MUST NOT contain query string parameters. The sender SHOULD treat the URI as opaque. There are several supported ways to refer to an SPSP endpoint:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't the spsp uri contain query parameters?

Copy link
Author

@sharafian sharafian Jan 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like a strange way to use the identifier. Visually, keeping special characters down is nice for readability. Forbidding reserved characters also lets people pass around payment-pointers like https://wallet.example.com/?receiver=$bob.example.com without worrying about escaping. It also creates some more complexity in how we resolve the identifier by adding more moving parts. Does $example.com?foo=bar resolve to https://example.com/.well-known/pay?foo=bar or https://example.com/?foo=bar? It would also preclude us from adding any query parameters as part of the resolution protocol, if we did that sometime in the future.

I don't think any of those are super strong reasons, but I also don't think the benefit is worth it.


- [Payment-pointer](../0026-payment-pointers/0026-payment-pointers.md) (Recommended) `$alice.example.com` or `$example.com/bob`. This SHOULD be the only kind of SPSP identifier exposed to users.
- Raw endpoint URI (Not recommended) `https://example.com/spsp/alice`. This SHOULD NOT be exposed to users, but SHOULD be supported by SPSP clients.

The SPSP Endpoint MUST respond to HTTPS `GET` requests in the following manner:

Expand All @@ -66,25 +72,30 @@ The SPSP Endpoint MUST respond to HTTPS `GET` requests in the following manner:
The sender queries the SPSP endpoint to get information about the type of payment that can be made to this receiver:

#### Request

(With the identifier `$example.com`)

``` http
GET /api/spsp/bob HTTP/1.1
Host: red.ilpdemo.org
Accept: application/json
GET /.well-known/pay HTTP/1.1
Host: example.com
Accept: application/spsp+json
```

#### Response
``` http
HTTP/1.1 200 OK
Content-Type: application/json
Content-Type: application/spsp+json

{
"destination_account": "example.ilpdemo.red.bob",
"shared_secret": "6jR5iNIVRvqeasJeCty6C-YB5X9FhSOUPCL_5nha5Vs",
"maximum_destination_amount": "100000",
"minimum_destination_amount": "10",
"ledger_info": {
"currency_code": "USD",
"currency_scale": 2
"shared_secret": "6jR5iNIVRvqeasJeCty6C+YB5X9FhSOUPCL/5nha5Vs=",
"balance": {
"maximum": "100000",
"current": "5360"
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a fundamental change, you're not looking up data about a person, but about an invoice. Although that may be useful by itself, that's not what we use spsp for in ilp-kit.

Can't we keep it so the sender determines the amount, like it's now in ilp-kit? Also, we need to display name and avatar.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The balance object is only present if this represents an invoice. The sender can still determine the amount; in the case of an invoice it will start to raise the current balance until it reaches the maximum, and if it's just an ordinary receiver then they'll just accept funds no matter what.

The name and avatar URL are still present in the receiver_info object. They're not required fields, though, because not everyone may want to display that information.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we need the current balance, even for invoices. In PSK2 the receiver tells the sender with every chunk how much of the total has arrived so far. I think it's more likely that you'd use that mechanism to figure out how close it is to being paid, rather than repeatedly querying the HTTP endpoint.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may want to see the current amount paid before you start trying to pay the invoice, though

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is a partially paid invoice something you'd expect to see regularly? It seems like that's more likely to be caused by something going wrong with a chunked payment half-way.

That also reminded me, is there a case to me made for using a specific payment ID in the PSK packet that's related to the invoice, or is the assumption that you would include the type of identifying information in the ILP address that SPSP returns?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you would probably use the ILP address to encode that information. However, when I upgraded http-ilp for PSK2, I used the payment ID as the Pay-Token rather than putting it in the PSK2 application data. Using the ILP address is a third option. We might want to decide which of those is the recommended option.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Evan, FWIW, partially paid invoices are a common business scenario, even aside from failed or incomplete payments.

"asset_info": {
"code": "USD",
"scale": 2
},
"receiver_info": {
"name": "Bob Dylan",
Expand All @@ -93,7 +104,18 @@ Content-Type: application/json
}
```

[Try this request](https://red.ilpdemo.org/api/receivers/bob)
The `balance`, `asset_info`, and `receiver_info` objects are all optional,
so a minimal SPSP response looks like:

``` http
HTTP/1.1 200 OK
Content-Type: application/spsp+json

{
"destination_account": "example.ilpdemo.red.bob",
"shared_secret": "6jR5iNIVRvqeasJeCty6C+YB5X9FhSOUPCL/5nha5Vs="
}
```

##### Response Headers

Expand All @@ -120,13 +142,14 @@ The response body is a JSON object that includes basic account details necessary
| Field | Type | Description |
|---|---|---|
| `destination_account` | [ILP Address](../0015-ilp-addresses/0015-ilp-addresses.md) | ILP Address of the receiver's account |
| `shared_secret` | 32 bytes, [base64-url encoded](https://en.wikipedia.org/wiki/Base64#URL_applications) | The shared secret to be used by this specific http client in the [Pre-Shared Key protocol](../0016-pre-shared-key/0016-pre-shared-key.md). Should be shared only by the server and this specific http client, and should therefore be different in each query response. |
| `maximum_destination_amount` | Integer String | Maximum amount, denoted in the minimum divisible units of the ledger, the receiver will accept. This amount may be determined by the ledger's maximum account balance or transfer amount. If the receiver expects a specific amount to be delivered, this may be set equal to the `minimum_destination_amount` |
| `minimum_destination_amount` | Integer String | Minimum amount, denoted in the minimum divisible units of the ledger, the receiver will accept. This amount may be determined by the minimum transfer amount of the ledger. If the receiver expects a specific amounts to be delivered, this may be set equal to the `maximum_destination_amount` |
| `ledger_info` | Object | Details about the destination ledger |
| `ledger_info.currency_code` | String | Currency code to identify the receiver's currency. Currencies that have [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) codes should use those. Sender UIs SHOULD be able to render non-standard codes |
| `ledger_info.currency_scale` | Integer | The scale of the amounts on the destination ledger (e.g. an amount of `"1000"` with a scale of `2` translates to `10.00` units of the destination ledger's currency) |
| `receiver_info` | Object | Arbitrary additional information about the receiver. This field has no schema and the receiver may include any fields they choose. The field names listed below are recommended merely for interoperability purposes. |
| `shared_secret` | 32 bytes, [base64 encoded](https://en.wikipedia.org/wiki/Base64) (including padding) | The shared secret to be used by this specific http client in the [Pre-Shared Key protocol](../0025-pre-shared-key-2/0025-pre-shared-key-2.md). Should be shared only by the server and this specific http client, and should therefore be different in each query response. |
| `balance` | Object | _(OPTIONAL)_ Details of this receiver's balance. Used for invoices and similar temporary accounts. |
| `balance.maximum` | Integer String | Maximum amount, denoted in the minimum divisible units of the receiver's account, which the receiver will accept. This represents the highest sum that incoming chunks are allowed to reach, not the highest size of an individual chunk (which is determined by path MTU). If this is an invoice the `balance.maximum` is the amount at which the invoice would be considered paid. |
| `balance.current` | Integer String | Current sum of all incoming chunks. |
| `asset_info` | Object | _(OPTIONAL)_ Details about the destination asset, for sender's display purposes. |
| `asset_info.code` | String | Asset code to identify the receiver's currency. Currencies that have [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) codes should use those. Sender UIs SHOULD be able to render non-standard codes |
| `asset_info.scale` | Integer | The scale of the amounts on the receiver's account (e.g. an amount of `"1000"` with a scale of `2` translates to `10.00` units of the receiver's asset/currency) |
| `receiver_info` | Object | _(OPTIONAL)_ Arbitrary additional information about the receiver. This field has no schema and the receiver may include any fields they choose. The field names listed below are recommended merely for interoperability purposes. |
| `receiver_info.name` | String | _(OPTIONAL)_ Full name of the individual, company or organization the receiver represents |
| `receiver_info.image_url` | HTTPS URL | _(OPTIONAL)_ URL where the sender can get a picture representation of the receiver |

Expand All @@ -148,46 +171,12 @@ Content-Type: application/json

### Payment Setup
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there use cases where you might want to send data along with the PSK payment? If so, SPSP should specify a format for the PSK data field.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite sure on this one. Supporting something like "source identifier" or "payment memo" might be useful. But there's a lot of use cases where we don't need that, so I think whatever we do I'd like to have the option to leave the application data blank.


The sender uses the receiver details to create the ILP packet:

* The `account` in the ILP packet is the `destination_account` provided by the receiver
* The `amount` is determined by the sender:

* The sender uses the `ledger_info.currency_scale` to determine the integer amount to include in the ILP packet (e.g. if they want the receiver to receive 10 units of `ledger_info.currency_code` on their ledger, a `ledger_info.currency_scale` value of 2 would indicate they should set the `amount` to 1000). In cases where the sender knows the integer amount the receiver should get, for example if that amount was requested by the receiver out of band, the sender does not need to use `ledger_info.currency_code` and `ledger_info.currency_scale`
* The sender SHOULD NOT set an amount greater than the `maximum_destination_amount` or lower than the `minimum_destination_amount`, as this will be rejected by the receiver
The sender uses the receiver details to create the PSK2 payment:

* The `data` is encoded using the [Pre-Shared Key protocol](../0016-pre-shared-key/0016-pre-shared-key.md):
* The `destination_account` should be used as the PSK2 destinationAccount.
* The `shared_secret` should be used as the PSK2 sharedSecret.
* If present, the `balance.maximum` SHOULD be used as the PSK2 chunked payment destination amount.

* The key is the `shared_secret` provided by the receiver
* Additional data may be included in the PSK details. SPSP data MUST be JSON.
* Private PSK headers SHOULD include `Content-Type: application/json` and `Content-Length: <byte length of data>`

Note that the sender can send as many payments as they want using the same receiver info. The sender SHOULD query the receiver again once the time indicated in the [`Cache-Control` header](#response-headers) has passed.

## Appendix A: (Optional) Webfinger Discovery

Whenever possible, receiver URLs should be exchanged out-of-band and discovery should be skipped. However, in some cases, it may be useful to have a standardized user-friendly identifier. This discovery method describes how to resolve such an identifier to an SPSP endpoint.

First, the sender uses Webfinger ([RFC 7033](https://tools.ietf.org/html/rfc7033)) to look up an identifier (e.g. `bob@red.ilpdemo.org`):

``` http
GET /.well-known/webfinger?resource=acct%3Abob%40red.ilpdemo.org HTTP/1.1
Host: red.ilpdemo.org
Accept: application/json
```
``` http
HTTP/1.1 200 OK
Content-Type: application/json

{
"subject": "acct:bob@red.ilpdemo.org",
"links": [
{
"rel": "https://interledger.org/rel/spsp/v2",
"href": "https://red.ilpdemo.org/api/spsp/bob"
}
]
}
```
[Try this request](https://red.ilpdemo.org/.well-known/webfinger?resource=acct%3Abob%40red.ilpdemo.org)
In a UI, the `asset_info` and `receiver_info` objects (if present) can be used for display purposes. These objects can be manipulated by the receiver in any way, so amounts SHOULD be displayed in source units when possible.

Note that the sender can send as many PSK2 payments as they want using the same receiver info. The sender SHOULD query the receiver again once the time indicated in the [`Cache-Control` header](#response-headers) has passed.