Coinbase API

30 minute read

Перевод статьи "Building a Coinbase App in Go"

Coinbase это потрясающий сервис, предоставляющий биткоин веб-кошельки для упрощения покупок и хранения биткоинов. Этот сервис предоставляет обширное API для третьесторонних девелоперов, которые могут создавать свои приложения поверх этой платформы. К сожалению, нет никаких Go-врапер над API, поэтому я взялся написать свой. В этой статье я расскажу вам, как написать простое приложение с использованием API и OAuth аутентификацией.

Готовая либа размещена на Github. В комплекте идет исчерпывающий список примеров, с описанием множества API вызовов.

Получаем API ключ для приложения

Любой пользователь coinbase может получить API ключ и секретный ключ, привязанные к его аккаунту. Используя эти доступы, можно программно использовать возможности аккаунта(это автоматизация покупки и продажи биткоинов, доступ к последним транзакциям, заказам и получателям, т.д.). В первой части этого мануала мы рассмотрим как узнать ваш текущий баланс используя пакет coinbase-go.

Добавляем пару ключей как переменные окружения

Прежде всего, нам необходимо сгенерировать пару ключей. Это можно сделать на странице настроек coinbase API. Убедитесь, что ключи включены, скопировав проверочный код из письма от coinbase.

Как закончите с этим, установите ключи как переменные окружения в вашем файле конфигурации для шела:

echo "export COINBASE_KEY='YOUR_KEY' " >> ~/.bash_profile
echo "export COINBASE_SECRET='YOUR_SECRET' " >> ~/.bash_profile

Обратите внимание, что у вас вместо .bash_profile может быть другой файл, например .bashrc. Все зависит от используемого шела(подробности тут).

После этого, нам необходимо перегрузить конфиг:

source ~/.bash_profile #  or .bashrc, etc.

Чтобы убедится, что все работает нормально, запустите в терминале:

echo $COINBASE_KEY

Если эта команда выведет наш coinbase ключ - мы на верном пути! Установка секретных данных как переменных окружения - хорошая практика в плане безопасности. Нам не нужно вписывать значения ключей в наших исходниках. Это важно, потому что если кто-то получить доступ к вашим API ключам, то он сможет сделать все что угодно с вашим аккаунтом.

Качаем пакет

Исходим из того что у нас уже есть установленный Go на машине. Убедитесь, что у вас правильно настроен $GOPATH. Если это не так, запустите команду ниже, указав путь к папке с установленным Go:

echo "export GOPATH='path/to/your/go/folder'" >> ~/.bash_profile

Теперь мы готовы скачивать пакет coinbase-go:

go get github.com/fabioberger/coinbase-go

Пишем приложение

Для демонстрации попробуем написать простое консольное приложение, которое будет показывать текущий баланс в coinbase. Давайте создадим новый проект в нашей рабочей директории, назовем его coinbase-app и создадим файл main.go

mkdir $GOPATH/src/coinbase-app
cd $GOPATH/src/coinbase-app
touch main.go

В файле main.go начнем писать наше приложение:

package main

import (
    "fmt"
    "log"
    "os"
    "github.com/fabioberger/coinbase-go"
)

func main() {

    c := coinbase.ApiKeyClient(os.Getenv("COINBASE_KEY"), os.Getenv("COINBASE_SECRET"))

    balance, err := c.GetBalance()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Balance is %f BTC", balance)
}

Это простое приложение импортирует пакет сoinbase-go совместно с fmt, log и os, которые входят в стандартную поставку Go. Внутри функции main мы получаем инстанс APIKeyClient, указывая пару ключей, которые мы указали как переменные окружения. После этого мы можем использовать переменную c для вызова методов и отправки запросов к Coinbases API. В этом примере мы вызываем метод GetBalance(), чтобы запросить наш текущий баланс с coinbase.

Для проверки этого примера запустите следующее:

go run main.go

Полный список доступных методов можно посмотреть на GoDoc. Так же, в README есть примеры как пользоваться этими методами.

OAuth аутентификация

Это здорово, что есть возможность взаимодействовать с нашей учетной записью на coinbase через API, особенно для автоматизации сделок и наблюдения за состоянием нашего аккаунта. Однако, еще круче если мы сможем создать приложение для управления любимы аккаунтами coinbase. Для этого нам нужно добавить поддержку OAuth в наше приложение.

Установка

Для начала нам нужно создать OAuth приложение на странице https://coinbase.com/oauth/applications. Нам обязательно нужно указать URI для редиректа(в нашем случае https://localhost:8443/tokens). Coinbase предоставляет нам ID клиента и секретный ключ клиента для нашего OAuth приложения, давайте добавим это в наш шел конфиг как переменные окружения:

echo "export COINBASE_CLIENT_ID='YOUR_CLIENT_ID' " >> ~/.bash_profile
echo "export COINBASE_CLIENT_SECRET='YOUR_CLIENT_SECRET' " >> ~/.bash_profile
source ~/.bash_profile # .bashrc, etc.

Вы помните, что при необходимости вам нужно заменить .bash_profile на ваш файл конфига для конкретного шела.

Мы уже скачали пакет coinbase-go, но если вы пропустили этот шаг, вернитесь к пункту "Качаем пакет" и выполните описанные инструкции. Чтобы написать пример веб-приложения мы будем использовать фреймворк Martini, для этого нам нужно его скачать:

go get github.com/go-martini/martini

Martini это мощный пакет для быстрого создания модульных веб-приложений/сервисов на Go. Мы будем использовать его для простого OAuth приложения, которое будет возвращать баланс для каждого coinbase пользователя, который будет аутентифицироваться через наше приложения.

Пишем приложение

Создадим новое приложение в нашей рабочей директории и назовем его coinbase-oauth и создадим файл main.go:

mkdir $GOPATH/src/coinbase-oauth
cd $GOPATH/src/coinbase-oauth
touch main.go

В файле main.go пишем код:

package main

import (
    "log"
    "net/http"
    "os"
    "strconv"
    "github.com/fabioberger/coinbase-go. "
    "github.com/go-martini/martini"
)

var o *coinbase.OAuth

func main() {
    // Настройка Martini
    m := martini.New()
    m.Use(martini.Logger())
    m.Use(martini.Recovery())
    m.Use(martini.Static("public"))
    r := martini.NewRouter()
    m.MapTo(r, (*martini.Routes)(nil))
    m.Action(r.Handle)
    // Инстанс OAuthService с указанными ID клиента и 
    // секретным ключем клиента
    o, err := coinbase.OAuthService(os.Getenv("COINBASE_CLIENT_ID"), 
              os.Getenv("COINBASE_CLIENT_SECRET"), 
              "https://localhost:8443/tokens")
    if err != nil {
        panic(err)
        return
    }

    // На странице http://localhosts:8443/ мы отображаем
    // ссылку на "authorize"
    r.Get("/", func() string {
        authorizeUrl := o.CreateAuthorizeUrl([]string{
            "all",
        })
        link := "<a href='" + authorizeUrl + "'>authorize</a>"
        return link
    })

    // После аутентификации, AuthorizeUrl редиректит 
    // на https://localhost:8443/tokens с указанным
    // параметром 'code'. Если все хорошо, отображается
    // пользователя
    r.Get("/tokens", func(res http.ResponseWriter, req *http.Request) string {

        // Получаем токены учитывая параметр 'code'
        tokens, err := o.NewTokensFromRequest(req) 
        // Мы используем 'code' параметр из req
        if err != nil {
            return err.Error()
        }

        // Инстанс OAuthClient с токенами
        c := coinbase.OAuthClient(tokens)

        // Запрашиваем баланс пользователя
        amount, err := c.GetBalance()
        if err != nil {
            return err.Error()
        }
        return strconv.FormatFloat(amount, 'f', 6, 64)
    })

    // HTTP
    go func() {
        if err := http.ListenAndServe(":8080", m); err != nil {
            log.Fatal(err)
        }
    }()

    // HTTPS
    // для генерации cert and key запустите указанный скрипт
    // в *nix терминале 
    // go run $(go env GOROOT)/src/pkg/crypto/tls/generate_cert.go 
    // --host="localhost"
    if err := http.ListenAndServeTLS(":8443", "cert.pem", "key.pem", m); err != nil {
        log.Fatal(err)
    }
}

Перед тем как мы сможем запустить написанный код, необходимо позаботиться о SSL, потому что сoinbase требует шифрованного соединения для передачи данных на наш редирект. Для использования HTTPS в рамках нашего сервера, необходимо сгенерировать девелоперский cert и key. К счастью, это делается довольно просто:

go run $(go env GOROOT)/src/pkg/crypto/tls/generate_cert.go --host="localhost"

Эта команда создаст cert.pem и key.pem файлы в нашей директории. Так как это самоподписанный сертификат, то браузер будет ругаться, что этому сертификату нельзя доверять. Однако, так как мы сами запустили этот сервер, то мы можем просто попросить браузер игнорировать это предупреждение и перейти к странице нашего приложения. Когда мы будем выкладывать нашу поделку в продакшен, нам нужно будет получить SSl сертификат из доверенного центра сертификации. Можем запускать наше приложение:

go run main.go

Чтобы посмотреть как это работает, перейдите в браузере на страницу https://localhost:8443/.

Окей, давайте немного поговорим о коде, который мы только что написали. Мы начали с импорта Martini, coinbase-go и нескольких других пакетов из стандартной библиотеки Go. Затем, в функции main мы создаем инстанс OAuthService с указанием ID клиента и секретным ключем, которые мы определили как переменные окружения. Последним параметром указываем адрес для редиректа. Теперь мы можем использовать o как экземпляр OAuthService по всему приложению.

Затем мы определили обработчик для запросов к http://localhost:8443/. Когда кто либо заходит по этому url, в приложении вызывается o.CreateAuthorizeUrl() для генерации ссылки на аутентификацию. Пользователю необходимо пройти по этой ссылки и авторизировать наше приложение, что бы мы смогли работать с его аккаунтом. Мы передаем в этот метод желаемые уровни доступа, которые нам необходимо запросить у пользователя(полный список уровней доступа). После этого сгенерированная ссылка на авторизацию в coinbase отображается на странице.

Когда пользователь кликает по этой ссылке и авторизирует наше приложение в coinbase, он отправляется на указанный редирект, который мы определили в вызове OAuthService(в нашем случае это https://localhost:8443/tokens). Срабатывает обработчик o.NewTokensFromRequest() который будет извлекать параметр code отправленный coinbase и запрашивать ассоциированные с ним токены.

Как только мы получим эти токены, будем использовать инстанс OAuthClient с указанием этих токенов. Этот клиент аналогичен ApiKeyClient и имеет доступ к тем же методам для отправки запросов к coinbase API. Единственное отличие в том, что каждый запрос будет от имени авторизированного пользователя. Следующий шаг - это вызов GetBalance() и отображение результата.

Токены, которые мы получили от coinbase, имеют свой срок жизни и узнать его мы можем обратившись к полю tokens.ExpireTime. После того как токены прогорели, нам нужно их обновить, вызвав функцию o.RefreshTokens(tokens).

Заключение

Я надеюсь, что рассмотренные примеры по созданию приложений для работы с coinbase API оказались полезными. Не забывайте поглядывать на примеры в README проекта на Github. Там описано большое количество различных запросов, которые можно совершать используя coinbase-go. Если у вас есть вопросы или комментарии, не стесняйтесь, пишите.