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

Improve & clarify interactive SEP-6 flow #342

Merged
merged 9 commits into from Jul 22, 2019
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
91 changes: 76 additions & 15 deletions ecosystem/sep-0006.md
Expand Up @@ -6,8 +6,8 @@ Title: Anchor/Client interoperability
Author: SDF
Status: Active
Created: 2017-10-30
Updated: 2019-06-21
Version 2.7.0
Updated: 2019-07-08
Version 3.0.0
```

## Simple Summary
Expand Down Expand Up @@ -276,32 +276,82 @@ Example:

Response code: `403 Forbidden`

An anchor that requires the user to fill out information on a webpage hosted by the anchor should use this response. A wallet that receives this response should open a popup browser window to the specified URL.
An anchor that requires the user to fill out information on a webpage hosted by the anchor should use this response. This can happen in situations where the anchor needs KYC information about a user, or when the anchor needs the user to perform a custom step for each transaction like entering an SMS code to confirm a withdrawal or selecting a bank account. A wallet that receives this response should open a popup browser window to the specified URL. The anchor must take care that the popup page displays well on a mobile device, as many wallets are phone apps. Next, the wallet must do one of two things:

The response body should be a JSON object with the following fields:
1. If the transaction is a deposit and the response does not contain `interactive_deposit` set to `true`, then the wallet must make a second request to `/deposit` with the same parameters to receive the deposit information and show it to the user. This is used in the case of an asynchronous deposit, where the user will initiate the deposit through some external system like bitcoin or a bank transfer at some later time. The user could initiate zero, one, or many deposits using the deposit information shown.
1. Otherwise, the wallet must either listen for a callback or poll the [`/transaction`](#single-historical-transaction) endpoint for updates about the transaction from the anchor. This is used for synchronous deposits or withdrawals. For synchronous deposits initiated in the anchor's flow, this allows the wallet to show the user status information and confirm if the deposit attempt initiated successfully or failed. For withdrawals, the wallet needs to get information on where to send the withdrawal payment to the anchor.

The response body must be a JSON object with the following fields:

Name | Type | Description
-----|------|------------
`type` | string | Always set to `interactive_customer_info_needed`
`url` | string | URL hosted by the anchor. The wallet should show this URL to the user either as a popup or an iframe.
`interactive_deposit` | boolean | (optional) Flag indicating that depositing is also handled in the anchor's interactive customer info flow. The wallet need not make additional requests to `/deposit` to complete the deposit. Defaults to `false`. Only relevant for responses to `/deposit` requests.
`id` | string | (optional) The anchor's internal ID for this deposit / withdrawal request. Can be passed to the [`/transaction`](#single-historical-transaction) endpoint to check status of the request.
Copy link
Member

Choose a reason for hiding this comment

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

I love this id field – it's definitely something that will enable us to track transactions more easily once they have moved from the /deposit or /withdraw flows.

My question is: should we be more explicit about when this id needs to be generated by the anchor? Is it only when the interactive KYC is completed? Or should an id be generated every time the endpoint is hit?

Here's a suggestion, which might make the wallet-anchor integration even simpler:

  1. We generate an id for each /deposit or /withdraw request, regardless of it being interactive or not
  2. On future calls to /deposit or /withdraw, the wallet also provides an id query param – this will remove the need for the Wallet to always replicate all request params (+ newly required data) until it gets a success response, since /deposit is not stateless anymore
  3. As soon as a 200 is received, the status of that deposit or withdrawal can be checked via /transaction by passing along that id

I think my reasoning is that we're basically doing a REST API around the /transactions resource, but using GET /withdraw or GET /deposit instead of POST /transactions, so it might make sense for them to share the same id.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks Alex, this makes sense. It's definitely a bummer that we need to be as backwards-compatible as possible, there are some strange choices in the existing SEP.

Here's what I'm thinking we can do for your suggestion:

  1. Add an optional id parameter to /deposit and /withdraw success responses. These are useful for calling GET /transaction. They don't help with repeated calls to /deposit or /withdraw, because clients won't make any more of those requests once they get a success response.
  2. After receiving a interactive_customer_info_needed response, the interactive_deposit: false case is the only one where the client needs to make another request to /deposit. We can specify that if the server has included an id, then the client doesn't need to re-include any parameters on its second call to /deposit, it just needs to supply the id.
  3. Add an optional id parameter to the non_interactive_customer_info_needed response, and specify that the client doesn't need to re-include any parameters if it supplies the id to future requests to /deposit or /withdraw.

Backwards compatibility analysis:

  • old client and new server: may not work, client will need to be updated to check for id if server depends on statefulness
  • new client and old server: will work since new clients check for id

However, I'm not too worried about this, because I don't think any existing flows are using the repeated requests to /deposit or /withdraw yet.

What do you think?

Copy link
Member

Choose a reason for hiding this comment

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

This approach makes sense to me! Thanks :)


If the wallet wants to be notified that the user has completed the required actions via the URL, it can add an extra `callback` parameter to the value of `url` before opening the browser window. If the `callback` value is a URL, the anchor should `POST` to it with a JSON message as the body once the user has successfully completed the process. If `callback=postMessage` is passed, the anchor should post a JSON message to `window.opener` via the Javascript [`Window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) method. If `window.opener` is undefined, the message should be posted to `window.parent` instead.

In either case, the JSON message should the same as the [Customer Information Status](#4-customer-information-status) response format, with one change. Since it's possible that the anchor is `POST`ing or `postMessaging` a success result, the `status` field make also be set to `success`.

Alternatively, the wallet can always poll the original deposit or withdrawal endpoint until a success, status `denied`, or error response is returned.

Example:
Example response:

```json
{
"type": "interactive_customer_info_needed",
"url" : "https://api.example.com/kycflow?account=GACW7NONV43MZIFHCOKCQJAKSJSISSICFVUJ2C6EZIW5773OU3HD64VI",
"interactive_deposit": true
"interactive_deposit": true,
"id": "82fhs729f63dh0v4"
}
```

#### Adding parameters to the URL

Before the wallet sends the user to the `url` field received from the anchor, it may add query parameters to the URL.

The possible parameters are summarized in the table below.

Name | Type | Description
-----|------|------------
`callback` | string | (optional) A URL that the anchor should `POST` a JSON message to when the user successfully completes the interactive flow. Can also be set to `postMessage`.
`jwt` | string | (optional) JWT previously obtained from the anchor via the [SEP-10](sep-0010.md) authentication flow

**`callback` details**

If the wallet wants to be notified that the user has completed the anchor's interactive flow (either success or failure), it can add this parameter to the URL. If the user abandons the process, the anchor does not need to report anything to the wallet. If the `callback` value is a URL, the anchor must `POST` to it with a JSON message as the body. If `callback=postMessage` is passed, the anchor must post a JSON message to `window.opener` via the Javascript [`Window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) method. If `window.opener` is undefined, the message must be posted to `window.parent` instead.

In either case, the JSON message should be identical to the response format for the [/transaction](#single-historical-transaction) endpoint.

As an alternative to using the `callback` parameter, the wallet can poll the transaction endpoint [`/transaction`](#single-historical-transaction) with the request `id` to check the status of the request.

**`jwt` details**

If the anchor's deposit or withdrawal endpoints require [SEP-10](sep-0010.md) authentication, the wallet must include this extra parameter. It allows the anchor to continue the interactive flow without requiring the user to authenticate again. When the user visits the anchor's interactive flow URL and provides a `jwt`, the anchor must verify the JWT, and if valid, honor the user's authenticated session. In this case, the anchor should immediately respond with a `302 redirect` response that sets a cookie authenticating the user and redirects the user to a different URL that does not contain the `jwt` parameter. This is to avoid the JWT appearing in the user's browser history. After the redirect, the authenticated interactive flow must continue as usual.

#### Completing an interactive withdrawal as the wallet

In the case of an interactive withdrawal, the user will interact with the anchor via the popup, entering information needed to complete the withdrawal like destination bank account or KYC details. Once the anchor has enough information to know how to complete the withdrawal, the wallet detect this and allow the user to complete the withdrawal inside the wallet's app. This is because a withdrawal is only properly initiated once the wallet transfers the correct amount of the withdrawal asset to the anchor's Stellar account.

To do this, the wallet needs to either poll the `/transaction` endpoint with the `id` provided in the `interactive_customer_info_needed` response from the anchor until the necessary information is available, or register a callback with the anchor as described above. Either way, the anchor's response will contain the transaction fields described in the [/transactions](#transaction-history) endpoint.

The wallet must use the response fields in the following way to complete the withdrawal:

- `status`: `incomplete` means the user is either still going through the interactive flow with the issuer, or has abandoned the transaction part way through. The wallet should simply keep polling or waiting in this case. The wallet should provide the user with an `x` or other way to abort the transaction and return to their wallet in this case. When the user aborts on the wallet side, the wallet must close the popup containing the anchor's flow as well.
- `status`: `pending_user_transfer_start` means the user has given all necessary info to the anchor. The next step is for the wallet to confirm the withdrawal with the user, and on confirmation to send a Stellar payment to the anchor's address.

When the wallet receives the `pending_user_transfer_start` `status`, it must use these fields from the response in the following ways:

- `withdraw_anchor_account`: send the withdrawal payment to this Stellar account
- `withdraw_memo`: use this memo in the payment transaction, if specified
- `withdraw_memo_type`: use this as the memo type

It should also use the following fields to show the user a summary of their withdrawal request when requesting final confirmation from the user:

- `to`: show the user what account they will be withdrawing to
- `external_extra_text`: show the bank name or store name that the user will be withdrawing their funds to

The anchor may chose to replace most of the digits in the `to` account number with `*`s to keep it confidential.

#### Completing an interactive deposit as the wallet



### 4. Customer Information Status

Response code: `403 Forbidden`
Expand All @@ -313,7 +363,7 @@ Name | Type | Description
`type` | string | Always set to `customer_info_status`
`status` | string | Status of customer information processing. One of: `pending`, `denied`
`more_info_url` | string | (optional) A URL the user can visit if they want more information about their account / status.
`eta` | int | (optional) Estimated number of seconds until the deposit status will update.
`eta` | int | (optional) Estimated number of seconds until the customer information status will update.

If the anchor decides that more customer information is needed after receiving some information and processing it, it can respond again with a response of type `interactive_customer_info_needed` or `non_interactive_customer_info_needed`. In the case of a `denied` request, an anchor can use the `more_info_url` to explain to the user the issue with their request and give them a way to rectify it manually. A wallet should show the `more_info_url` to the user when explaining that the request was denied.

Expand Down Expand Up @@ -564,11 +614,20 @@ Name | Type | Description
`kind` | string | `deposit` or `withdrawal`
`status` | string | Processing status of deposit/withdrawal
`status_eta` | number | (optional) Estimated number of seconds until a status change is expected
`more_info_url` | string | (optional) A URL the user can visit if they want more information about their account / status.
`amount_in` | string | (optional) Amount received by anchor at start of transaction as a string with up to 7 decimals. Excludes any fees charged before the anchor received the funds.
`amount_out` | string | (optional) Amount sent by anchor to user at end of transaction as a string with up to 7 decimals. Excludes amount converted to XLM to fund account and any external fees
`amount_fee` | string | (optional) Amount of fee charged by anchor
`from` | string | (optional) Sent from address (perhaps BTC or bank in the case of a deposit, stellar address in the case of a withdrawal)
`to` | string | (optional) Sent to address (perhaps BTC or bank in the case of a withdrawal, stellar address in the case of a deposit)
`from` | string | (optional) Sent from address (perhaps BTC, IBAN, or bank account in the case of a deposit, Stellar address in the case of a withdrawal)
`to` | string | (optional) Sent to address (perhaps BTC, IBAN, or bank account in the case of a withdrawal, Stellar address in the case of a deposit)
`external_extra` | string | (optional) extra information for the external account involved. It could be a bank routing number, BIC, or store number for example.
`external_extra_text` | string | (optional) Text version of `external_extra`. This is the name of the bank or store.
`deposit_memo` | string | (optional) if this is a deposit, this is the memo (if any) used to transfer the asset to the `to` Stellar address
`deposit_memo_type` | string | (optional) type for the `deposit_memo`
`withdraw_anchor_account` | string | (optional) If this is a withdrawal, this is the anchor's Stellar account that the user transferred (or will transfer) their issued asset to
`withdraw_memo` | string | (optional) Memo used when the user transferred to `withdraw_anchor_account`
`withdraw_memo_type` | string | (optional) Memo type for `withdraw_memo`

`started_at` | UTC ISO 8601 string | (optional) start date and time of transaction
`completed_at` | UTC ISO 8601 string | (optional) completion date and time of transaction
`stellar_transaction_id` | string | (optional) transaction_id on Stellar network of the transfer that either completed the deposit or started the withdrawal
Expand All @@ -584,6 +643,8 @@ Name | Type | Description
* `pending_stellar` -- deposit/withdrawal operation has been submitted to Stellar network, but is not yet confirmed
* `pending_trust` -- the user must add a trust-line for the asset for the deposit to complete
* `pending_user` -- the user must take additional action before the deposit / withdrawal can complete
* `pending_user_transfer_start` -- the user has not yet initiated their transfer to the anchor. This is the necessary first step in any deposit or withdrawal flow.
* `incomplete` -- there is not yet enough information for this transaction to be initiated. Perhaps the user has not yet entered the amount they want to transfer in an interactive flow.
* `no_market` -- could not complete deposit because no satisfactory asset/XLM market was available to create the account
* `too_small` -- deposit/withdrawal size less than `min_amount`
* `too_large` -- deposit/withdrawal size exceeded `max_amount`
Expand Down