Support KYC Registration

As defined by the SEP-12 KYC API specification,

This SEP defines a standard way for stellar clients to upload KYC (or other) information to anchors and other services. SEP-6 and SEP-31 use this protocol, but it can serve as a stand-alone service as well.

Polaris supports this API but requires the anchor to define and validate the KYC values required from each user. This definition and validation is added by implementing the CustomerIntegration class.

Configure Settings

Add SEP-12 to the ACTIVE_SEPS environment variable or setting.

ACTIVE_SEPS=sep-1,sep-10,sep-6,sep-12
HOST_URL=http://localhost:8000
LOCAL_MODE=1
ENABLE_SEP_0023=1
SIGNING_SEED=S...
SERVER_JWT_KEY=...

Integrations

Defining Different Customer Types

SEP-12 has a very important type parameter that allows the anchor to define different requirements for different users, depending on the type of transaction the user wants to initate.

These type values are defined in the GET /info endpoints of the SEPs that facilitate transactions, such as SEP-6 and SEP-31. See the default_info_func() and info() functions for more information.

Identifying Customers

Customers are identified three different ways:

  • A Stellar account (G…)
    • A customer that only provides a Stellar account can be assumed to be the sole owner or user of the account.

  • A muxed account (M…)
    • A customer that provides a muxed account is a user of a shared Stellar account. Muxed addresses are simply a Stellar account encoded with an integer memo.

  • A Stellar account & memo (G…, 1183491)
    • A customer that provides both a Stellar account and memo is also a user of a shared Stellar account. Unlike muxed accounts, the memos used in this scheme can be an integer, a base-64-encoded hash, or string of text.

The data model you create to organize customers’ KYC information and associated transactions must support identifying customers using any of these methods.

Note

Customers can use different applications connected to Stellar, and therefore may have multiple Stellar accounts, muxed accounts, and memos. Your data model must account for this possibility as well.

Providing Customer Statuses

Client applications will call the GET /customer endpoint to fetch the status of a customer (user).

If the custom specified doesn’t exist or requires additional KYC information in order to be approved, the fields required should be returned from get(). Customers can also be in accepted, pending, rejected states. See the function documentation for specific parameter and return types.

from typing import Dict, List
from polaris.integrations import CustomerIntegration
from polaris.sep10.token import SEP10Token
from polaris.models import Transaction
from rest_framework.request import Request
from .user import user_for_account, fields_for_type

class AnchorCustomer(CustomerIntegration):
    def get(
        self,
        token: SEP10Token,
        request: Request,
        params: Dict,
        *args: List,
        **kwargs: Dict
    ) -> Dict:
        user = user_for_account(
            token.muxed_account or token.account,
            token.memo or params.get("memo"),
            "id" if token.memo else params.get("memo_type")
        )
        fields = fields_for_type(params.get("type"))
        if not user:
            return {
                "status": "NEEDS_INFO",
                "fields": fields
            }
        missing_fields = dict([
            (f, v) for f, v in fields.items()
            if not getattr(user, f, False)
        ])
        provided_fields = dict([
            (f, v) for f, v in fields.items()
            if getattr(user, f, False)
        ])
        if missing_fields:
            return {
                "id": user.id,
                "status": "NEEDS_INFO",
                "fields": missing_fields,
                "provided_fields": provided_fields
            }
        if user.rejected:
            return {
                "id": user.id,
                "status": "REJECTED",
                "provided_fields": provided_fields
            }
        if user.kyc_approved:
            return {
                "id": user.id,
                "status": "APPROVED",
                "provided_fields": provided_fields
            }
        return {
            "id": user.id,
            "status": "PENDING",
            "provided_fields": provided_fields
        }

Validating Customer KYC

Once client applications have collected the KYC information requested by the anchor, they’ll make a PUT /customer to send that information to the anchor.

Using the put() method, the anchor is resposible for validating and saving the customer information provided and returning a ID that the client can use in future requests. If the information isn’t valid, a ValueError should be raised with a message explaining why.

Deleting Customer Records

Users may want to have the anchor delete their KYC information previously provided. Anchors can use the delete() method to delete the information immediately or schedule the customer’s information for deletion if necessary to comply with local regulations.

Sending Customer Status Callbacks

Some client applications may want to receive push-style notifications when a customer’s KYC status has changed. While optional, it is recommended that the anchor implements the callback() method, which is used to save the URL the client application would like to receive callback requests at for the customer specified via an ID.

Register Integrations

Anchors need to register their CustomerIntegration subclass via register_integrations().

from django.apps import AppConfig

class AnchorConfig(AppConfig):
    name = 'anchor'

    def ready(self):
        from polaris.integrations import register_integrations
        ...
        from .sep12 import AnchorCustomer

        register_integrations(
            ...
            customer=AnchorCustomer()
        )

Testing with the Demo Wallet

When used in the context of a SEP-6 or SEP-31 transaction, the anchor can test their implementation of SEP-12. See SEP-6’s instructions for Testing with the Demo Wallet.