├── vendor └── .DS_Store ├── .gitignore ├── glide.yaml ├── go.mod ├── api ├── randstr.go ├── apicaller.go └── request.go ├── conf └── mockserver.app.conf ├── preinit └── preinit.go ├── mockserver.go ├── models ├── setup.go └── apicode.go ├── go.sum ├── routers └── router.go ├── controllers ├── MerchantController.go └── OuterController.go ├── LICENSE └── merchant ├── README.md └── README.html /vendor/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CYBAVO/sofa_mock_server/HEAD/vendor/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | -------------------------------------------------------------------------------- /glide.yaml: -------------------------------------------------------------------------------- 1 | package: github.com/cybavo/SOFA_MOCK_SERVER 2 | import: 3 | - package: github.com/astaxie/beego 4 | version: v1.11.1 5 | subpackages: 6 | - logs 7 | - orm 8 | - plugins/cors 9 | - package: github.com/mattn/go-sqlite3 10 | version: v1.10.0 11 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cybavo/SOFA_MOCK_SERVER 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/astaxie/beego v1.11.1 7 | github.com/mattn/go-sqlite3 v1.10.0 8 | ) 9 | 10 | require ( 11 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 // indirect 12 | google.golang.org/appengine v1.6.7 // indirect 13 | gopkg.in/yaml.v2 v2.4.0 // indirect 14 | ) 15 | -------------------------------------------------------------------------------- /api/randstr.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | ) 7 | 8 | const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 9 | 10 | var seededRand *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano())) 11 | 12 | func RandomString(length int) string { 13 | b := make([]byte, length) 14 | for i := range b { 15 | b[i] = charset[seededRand.Intn(len(charset))] 16 | } 17 | return string(b) 18 | } 19 | -------------------------------------------------------------------------------- /conf/mockserver.app.conf: -------------------------------------------------------------------------------- 1 | appname = SOFA_MOCK_SERVER 2 | 3 | ########Deploy flag############## 4 | #runmode prod or dev 5 | runmode = dev 6 | # 0: debug, stage. 1: release 7 | DeployMode = "${DEPLOY_MODE||0}" 8 | 9 | 10 | 11 | #########HTTP default bind all ip################### 12 | EnableHTTP = "${SRV_HTTP_ENABLE||true}" 13 | HTTPAddr = "${SRV_HTTP_ADDR||0.0.0.0}" 14 | HTTPPort = "${SRV_HTTP_PORT||8889}" 15 | 16 | copyrequestbody = true 17 | 18 | ####################MySQL 数据库配置########################### 19 | db_adapter=sqlite3 20 | 21 | ####################sqlite3 数据库配置########################### 22 | db_database=./runtime/mocksrv.db 23 | 24 | ####################WW Server URL########################### 25 | api_server_url="" 26 | -------------------------------------------------------------------------------- /preinit/preinit.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2022 The CYBAVO developers 2 | // All Rights Reserved. 3 | // NOTICE: All information contained herein is, and remains 4 | // the property of CYBAVO and its suppliers, 5 | // if any. The intellectual and technical concepts contained 6 | // herein are proprietary to CYBAVO 7 | // Dissemination of this information or reproduction of this materia 8 | // is strictly forbidden unless prior written permission is obtained 9 | // from CYBAVO. 10 | 11 | package preinit 12 | 13 | import ( 14 | "os" 15 | 16 | "github.com/astaxie/beego" 17 | "github.com/astaxie/beego/logs" 18 | ) 19 | 20 | const ( 21 | ConfigurationFile = "conf/mockserver.app.conf" 22 | ) 23 | 24 | func init() { 25 | logs.Info("LoadAppConfig %s", ConfigurationFile) 26 | err := beego.LoadAppConfig("ini", ConfigurationFile) 27 | 28 | if err != nil { 29 | logs.Error("LoadAppConfig,An error occurred:", err) 30 | os.Exit(1) 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /mockserver.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2022 The CYBAVO developers 2 | // All Rights Reserved. 3 | // NOTICE: All information contained herein is, and remains 4 | // the property of CYBAVO and its suppliers, 5 | // if any. The intellectual and technical concepts contained 6 | // herein are proprietary to CYBAVO 7 | // Dissemination of this information or reproduction of this materia 8 | // is strictly forbidden unless prior written permission is obtained 9 | // from CYBAVO. 10 | 11 | package main 12 | 13 | import ( 14 | "github.com/astaxie/beego" 15 | "github.com/astaxie/beego/plugins/cors" 16 | "github.com/cybavo/SOFA_MOCK_SERVER/models" 17 | _ "github.com/cybavo/SOFA_MOCK_SERVER/preinit" 18 | _ "github.com/cybavo/SOFA_MOCK_SERVER/routers" 19 | ) 20 | 21 | func main() { 22 | 23 | models.RegisterDataBase() 24 | models.RegisterModel() 25 | enableCORS() 26 | 27 | beego.Run() 28 | } 29 | 30 | func enableCORS() { 31 | v := beego.AppConfig.DefaultBool("enable_cors", true) 32 | if v { 33 | beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{ 34 | AllowAllOrigins: true, 35 | AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, 36 | AllowHeaders: []string{"Origin", "Authorization", "Access-Control-Allow-Origin", "Content-Type", "X-LOGIN-TOKEN"}, 37 | ExposeHeaders: []string{"Content-Length", "Access-Control-Allow-Origin"}, 38 | })) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /models/setup.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2022 The CYBAVO developers 2 | // All Rights Reserved. 3 | // NOTICE: All information contained herein is, and remains 4 | // the property of CYBAVO and its suppliers, 5 | // if any. The intellectual and technical concepts contained 6 | // herein are proprietary to CYBAVO 7 | // Dissemination of this information or reproduction of this materia 8 | // is strictly forbidden unless prior written permission is obtained 9 | // from CYBAVO. 10 | 11 | package models 12 | 13 | import ( 14 | "os" 15 | "path/filepath" 16 | "time" 17 | 18 | "github.com/astaxie/beego" 19 | "github.com/astaxie/beego/logs" 20 | "github.com/astaxie/beego/orm" 21 | _ "github.com/mattn/go-sqlite3" 22 | ) 23 | 24 | func GetMockDatabasePrefix() string { 25 | return beego.AppConfig.DefaultString("db_prefix", "mock_") 26 | } 27 | 28 | func RegisterDataBase() { 29 | logs.Info("Init Database Configuration.") 30 | 31 | adapter := beego.AppConfig.DefaultString("db_adapter", "") 32 | 33 | if adapter == "sqlite3" { 34 | orm.DefaultTimeLoc = time.UTC 35 | database := beego.AppConfig.DefaultString("db_database", "") 36 | 37 | dbPath := filepath.Dir(database) 38 | err := os.MkdirAll(dbPath, 0750) 39 | if err != nil { 40 | logs.Error("Failed to mkdir =>", err) 41 | } 42 | err = orm.RegisterDataBase("default", "sqlite3", database) 43 | 44 | if err != nil { 45 | logs.Error("sqlite3 Register Database Fail:", err) 46 | } 47 | } else { 48 | logs.Error("DB Non support type:", adapter) 49 | os.Exit(1) 50 | } 51 | logs.Info("Complete the database init:", adapter) 52 | } 53 | 54 | func RegisterModel() { 55 | 56 | orm.RegisterModelWithPrefix(GetMockDatabasePrefix(), 57 | new(APICode), 58 | ) 59 | err := orm.RunSyncdb("default", false, true) 60 | if err != nil { 61 | logs.Warning("Failed to RunSyncdb => ", err) 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /models/apicode.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2022 The CYBAVO developers 2 | // All Rights Reserved. 3 | // NOTICE: All information contained herein is, and remains 4 | // the property of CYBAVO and its suppliers, 5 | // if any. The intellectual and technical concepts contained 6 | // herein are proprietary to CYBAVO 7 | // Dissemination of this information or reproduction of this materia 8 | // is strictly forbidden unless prior written permission is obtained 9 | // from CYBAVO. 10 | 11 | package models 12 | 13 | import ( 14 | "github.com/astaxie/beego/logs" 15 | "github.com/astaxie/beego/orm" 16 | ) 17 | 18 | type APICode struct { 19 | APICodeID int64 `orm:"pk;auto;unique;column(api_code_id)" json:"api_code_id"` 20 | APICode string `orm:"unique;column(api_code)" json:"api_code"` 21 | ApiSecret string `orm:"unique;column(api_secret)" json:"api_secret"` 22 | WalletID int64 `orm:"unique;column(wallet_id)" json:"wallet_id"` 23 | } 24 | 25 | func (m *APICode) TableName() string { 26 | return "apicode" 27 | } 28 | 29 | func (m *APICode) TableEngine() string { 30 | return "INNODB" 31 | } 32 | 33 | func (m *APICode) TableNameWithPrefix() string { 34 | return GetMockDatabasePrefix() + m.TableName() 35 | } 36 | 37 | func SetAPICode(apiCodeObj *APICode) (err error) { 38 | o := orm.NewOrm() 39 | 40 | existedAPICodeObj, err := GetWalletAPICode(apiCodeObj.WalletID) 41 | if err != nil { 42 | apiCodeObj.APICodeID, err = o.Insert(apiCodeObj) 43 | if err != nil { 44 | logs.Error("Failed to insert API token =>", err) 45 | return 46 | } 47 | } else { 48 | apiCodeObj.APICodeID = existedAPICodeObj.APICodeID 49 | _, err = o.Update(apiCodeObj, "api_secret", "api_code") 50 | if err != nil { 51 | logs.Warning("Failed to update API secret =>", err) 52 | return 53 | } 54 | } 55 | logs.Info("Succeeded to set API token =>", apiCodeObj) 56 | return 57 | } 58 | 59 | func GetWalletAPICode(walletID int64) (apiCodeObj *APICode, err error) { 60 | o := orm.NewOrm() 61 | 62 | apiCodeObj = &APICode{} 63 | err = o.QueryTable(apiCodeObj.TableNameWithPrefix()). 64 | Filter("wallet_id", walletID). 65 | One(apiCodeObj) 66 | 67 | if err != nil { 68 | // try read-only API code 69 | err = o.QueryTable(apiCodeObj.TableNameWithPrefix()). 70 | Filter("wallet_id", 0). 71 | One(apiCodeObj) 72 | } 73 | return 74 | } 75 | -------------------------------------------------------------------------------- /api/apicaller.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2022 The CYBAVO developers 2 | // All Rights Reserved. 3 | // NOTICE: All information contained herein is, and remains 4 | // the property of CYBAVO and its suppliers, 5 | // if any. The intellectual and technical concepts contained 6 | // herein are proprietary to CYBAVO 7 | // Dissemination of this information or reproduction of this materia 8 | // is strictly forbidden unless prior written permission is obtained 9 | // from CYBAVO. 10 | 11 | package api 12 | 13 | import ( 14 | "bytes" 15 | "crypto/sha256" 16 | "encoding/base64" 17 | "encoding/json" 18 | "errors" 19 | "fmt" 20 | "io/ioutil" 21 | "net/http" 22 | "sort" 23 | "strings" 24 | "time" 25 | 26 | "github.com/astaxie/beego" 27 | "github.com/astaxie/beego/logs" 28 | "github.com/cybavo/SOFA_MOCK_SERVER/models" 29 | ) 30 | 31 | var baseURL = beego.AppConfig.DefaultString("api_server_url", "") 32 | 33 | func buildChecksum(params []string, secret string, time int64, r string) string { 34 | params = append(params, fmt.Sprintf("t=%d", time)) 35 | params = append(params, fmt.Sprintf("r=%s", r)) 36 | sort.Strings(params) 37 | params = append(params, fmt.Sprintf("secret=%s", secret)) 38 | return fmt.Sprintf("%x", sha256.Sum256([]byte(strings.Join(params, "&")))) 39 | } 40 | 41 | func MakeRequest(targetID int64, method string, api string, params []string, postBody []byte) ([]byte, error) { 42 | if targetID < 0 || method == "" || api == "" { 43 | return nil, errors.New("invalid parameters") 44 | } 45 | 46 | client := &http.Client{} 47 | r := RandomString(8) 48 | if r == "" { 49 | return nil, errors.New("can't generate random byte string") 50 | } 51 | t := time.Now().Unix() 52 | url := fmt.Sprintf("%s%s?t=%d&r=%s", baseURL, api, t, r) 53 | if len(params) > 0 { 54 | url += fmt.Sprintf("&%s", strings.Join(params, "&")) 55 | } 56 | var req *http.Request 57 | var err error 58 | if postBody == nil || len(postBody) == 0 { 59 | req, err = http.NewRequest(method, url, nil) 60 | } else { 61 | req, err = http.NewRequest(method, url, bytes.NewReader(postBody)) 62 | params = append(params, string(postBody)) 63 | } 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | apiCodeObj, err := models.GetWalletAPICode(targetID) 69 | if err != nil { 70 | return nil, err 71 | } 72 | 73 | req.Header.Set("X-API-CODE", apiCodeObj.APICode) 74 | req.Header.Set("X-CHECKSUM", buildChecksum(params, apiCodeObj.ApiSecret, t, r)) 75 | if postBody != nil { 76 | req.Header.Set("Content-Type", "application/json") 77 | } 78 | req.Header.Set("User-Agent", "golang") 79 | logs.Debug("Request URL:", url) 80 | logs.Debug("\tX-CHECKSUM:\t", req.Header.Get("X-CHECKSUM")) 81 | 82 | res, err := client.Do(req) 83 | if err != nil { 84 | return nil, err 85 | } 86 | defer res.Body.Close() 87 | 88 | body, err := ioutil.ReadAll(res.Body) 89 | if err != nil { 90 | return nil, err 91 | } 92 | if res.StatusCode != 200 { 93 | result := &ErrorCodeResponse{} 94 | _ = json.Unmarshal(body, result) 95 | msg := fmt.Sprintf("%s, Error: %s", res.Status, result.String()) 96 | return body, errors.New(msg) 97 | } 98 | 99 | // 100 | // verify checksum of a successful response 101 | // 102 | checksum := res.Header.Get("X-CHECKSUM") 103 | payload := string(body) + apiCodeObj.ApiSecret 104 | sha, _ := CalcSHA256([]byte(payload)) 105 | checksumVerf := base64.URLEncoding.EncodeToString(sha) 106 | if checksum != checksumVerf { 107 | return nil, errors.New("mismatched response checksum") 108 | } 109 | return body, nil 110 | } 111 | 112 | func CalcSHA256(data []byte) (calculatedHash []byte, err error) { 113 | sha := sha256.New() 114 | _, err = sha.Write(data) 115 | if err != nil { 116 | return 117 | } 118 | calculatedHash = sha.Sum(nil) 119 | return 120 | } 121 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= 2 | github.com/astaxie/beego v1.11.1 h1:6DESefxW5oMcRLFRKi53/6exzup/IR6N4EzzS1n6CnQ= 3 | github.com/astaxie/beego v1.11.1/go.mod h1:i69hVzgauOPSw5qeyF4GVZhn7Od0yG5bbCGzmhbWxgQ= 4 | github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ= 5 | github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU= 6 | github.com/belogik/goes v0.0.0-20151229125003-e54d722c3aff/go.mod h1:PhH1ZhyCzHKt4uAasyx+ljRCgoezetRNf59CUtwUkqY= 7 | github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60= 8 | github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE= 9 | github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= 10 | github.com/couchbase/go-couchbase v0.0.0-20181122212707-3e9b6e1258bb/go.mod h1:TWI8EKQMs5u5jLKW/tsb9VwauIrMIxQG1r5fMsswK5U= 11 | github.com/couchbase/gomemcached v0.0.0-20181122193126-5125a94a666c/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c= 12 | github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= 13 | github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY= 14 | github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= 15 | github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk= 16 | github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= 17 | github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= 18 | github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= 19 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 20 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 21 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 22 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 23 | github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= 24 | github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= 25 | github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 26 | github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= 27 | github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= 28 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 29 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 30 | github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw= 31 | github.com/siddontang/ledisdb v0.0.0-20181029004158-becf5f38d373/go.mod h1:mF1DpOSOUiJRMR+FDqaqu3EBqrybQtrDDszLUZ6oxPg= 32 | github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA= 33 | github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE= 34 | github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= 35 | github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc= 36 | golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 37 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= 38 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 39 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 40 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 41 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 42 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 43 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 44 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 45 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 46 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 47 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 48 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 49 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 50 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 51 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 52 | -------------------------------------------------------------------------------- /api/request.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2022 The CYBAVO developers 2 | // All Rights Reserved. 3 | // NOTICE: All information contained herein is, and remains 4 | // the property of CYBAVO and its suppliers, 5 | // if any. The intellectual and technical concepts contained 6 | // herein are proprietary to CYBAVO 7 | // Dissemination of this information or reproduction of this materia 8 | // is strictly forbidden unless prior written permission is obtained 9 | // from CYBAVO. 10 | 11 | package api 12 | 13 | import ( 14 | "encoding/json" 15 | "fmt" 16 | ) 17 | 18 | type CommonResponse struct { 19 | Result int64 `json:"result"` 20 | } 21 | 22 | type ErrorCodeResponse struct { 23 | ErrMsg string `json:"error,omitempty"` 24 | ErrCode int `json:"error_code,omitempty"` 25 | Message string `json:"message,omitempty"` 26 | Blacklist map[string][]string `json:"blacklist,omitempty"` 27 | } 28 | 29 | func (m *ErrorCodeResponse) String() string { 30 | if len(m.Blacklist) > 0 { 31 | blacklist, _ := json.Marshal(m.Blacklist) 32 | return fmt.Sprintf("%s (msg:%s) (code:%d)", m.ErrMsg, string(blacklist), m.ErrCode) 33 | } 34 | return fmt.Sprintf("%s (msg:%s) (code:%d)", m.ErrMsg, m.Message, m.ErrCode) 35 | } 36 | 37 | type SetAPICodeRequest struct { 38 | APICode string `json:"api_code"` 39 | ApiSecret string `json:"api_secret"` 40 | } 41 | 42 | type CallbackType int 43 | 44 | const ( 45 | DepositCallback CallbackType = 1 46 | WithdrawCallback CallbackType = 2 47 | CollectCallback CallbackType = 3 48 | AirdropCallback CallbackType = 4 49 | ) 50 | 51 | type ProcessingState int8 52 | 53 | const ( 54 | ProcessingStateUndefined ProcessingState = -1 55 | ProcessingStateInPool ProcessingState = 0 56 | ProcessingStateInChain ProcessingState = 1 57 | ProcessingStateDone ProcessingState = 2 58 | ) 59 | 60 | type CallbackState int64 61 | 62 | const ( 63 | CallbackStateHolding CallbackState = 1 // Processing batch in KMS (1) 64 | CallbackStateInPool CallbackState = 2 // KMS process done, TXID created (2) 65 | CallbackStateInChain CallbackState = 3 // TXID in chain (3) 66 | CallbackStateFailed CallbackState = 5 // Failed (5) 67 | CallbackStateCancelled CallbackState = 8 // cancelled 68 | CallbackStateDropped CallbackState = 10 // Dropped 69 | CallbackStateInChainFailed CallbackState = 11 // Transaction Failed (11) 70 | ) 71 | 72 | const ( 73 | MerchantOrderStatePending = -1 74 | MerchantOrderStateSuccess = 0 75 | MerchantOrderStateExpired = 1 76 | MerchantOrderStateInsufficient = 2 77 | MerchantOrderStateExcess = 3 78 | MerchantOrderStateCancel = 4 79 | ) 80 | 81 | type CallbackStruct struct { 82 | Type int `json:"type"` 83 | Serial int64 `json:"serial"` 84 | OrderID string `json:"order_id"` 85 | Currency string `json:"currency"` 86 | TXID string `json:"txid"` 87 | BlockHeight int64 `json:"block_height"` 88 | TIndex int `json:"tindex"` 89 | VOutIndex int `json:"vout_index"` 90 | Amount string `json:"amount"` 91 | Fees string `json:"fees"` 92 | Memo string `json:"memo"` 93 | BroadcastAt int64 `json:"broadcast_at"` 94 | ChainAt int64 `json:"chain_at"` 95 | FromAddress string `json:"from_address"` 96 | ToAddress string `json:"to_address"` 97 | WalletID int64 `json:"wallet_id"` 98 | State CallbackState `json:"state"` 99 | ConfirmBlocks int64 `json:"confirm_blocks"` 100 | ProcessingState ProcessingState `json:"processing_state"` 101 | Addon map[string]interface{} `json:"addon"` 102 | Decimals int `json:"decimal"` 103 | } 104 | 105 | type MerchantCallbackStruct struct { 106 | MerchantID int64 `json:"merchant_id"` 107 | OrderID string `json:"order_id"` 108 | Currency string `json:"currency"` 109 | TXID string `json:"txid"` 110 | RecvAmount string `json:"recv_amount"` 111 | BroadcastAt int64 `json:"broadcast_at"` 112 | BlockHeight int64 `json:"block_height"` 113 | FromAddress string `json:"from_address"` 114 | ToAddress string `json:"to_address"` 115 | State int64 `json:"state"` 116 | Addon map[string]interface{} `json:"addon"` 117 | CurrencyBIP44 int64 `json:"currency_bip44"` 118 | TokenAddress string `json:"token_address"` 119 | Fee string `json:"fee"` 120 | Decimals int `json:"decimal"` 121 | FeeDecimals int `json:"fee_decimal"` 122 | } 123 | 124 | type RequestPaymentOrderRequest struct { 125 | Currency int64 `json:"currency"` 126 | TokenAddress string `json:"token_address"` 127 | Amount string `json:"amount"` 128 | Duration int64 `json:"duration"` 129 | Description string `json:"description"` 130 | RedirectURL string `json:"redirect_url"` 131 | OrderID string `json:"order_id"` 132 | } 133 | 134 | type QueryPaymentOrderResponse struct { 135 | Address string `json:"address"` 136 | State int8 `json:"state"` 137 | TXID string `json:"tx_id"` 138 | ExpiredTime int64 `json:"expired_time"` 139 | RedirectURL string `json:"redirect_url"` 140 | } 141 | -------------------------------------------------------------------------------- /routers/router.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2022 The CYBAVO developers 2 | // All Rights Reserved. 3 | // NOTICE: All information contained herein is, and remains 4 | // the property of CYBAVO and its suppliers, 5 | // if any. The intellectual and technical concepts contained 6 | // herein are proprietary to CYBAVO 7 | // Dissemination of this information or reproduction of this materia 8 | // is strictly forbidden unless prior written permission is obtained 9 | // from CYBAVO. 10 | 11 | package routers 12 | 13 | import ( 14 | "github.com/astaxie/beego" 15 | "github.com/cybavo/SOFA_MOCK_SERVER/controllers" 16 | ) 17 | 18 | func init() { 19 | beego.Router("/v1/mock/wallets/:wallet_id/apitoken", &controllers.OuterController{}, "POST:SetAPIToken") 20 | beego.Router("/v1/mock/wallets/:wallet_id/addresses", &controllers.OuterController{}, "POST:CreateDepositWalletAddresses") 21 | beego.Router("/v1/mock/wallets/:wallet_id/addresses", &controllers.OuterController{}, "GET:GetDepositWalletAddresses") 22 | beego.Router("/v1/mock/wallets/:wallet_id/addresses/label", &controllers.OuterController{}, "POST:UpdateDepositWalletAddressLabel") 23 | beego.Router("/v1/mock/wallets/:wallet_id/addresses/get_labels", &controllers.OuterController{}, "POST:GetDepositWalletAddressesLabel") 24 | beego.Router("/v1/mock/wallets/:wallet_id/receiver/addresses/verify", &controllers.OuterController{}, "POST:VerifyDepositAddresses") 25 | beego.Router("/v1/mock/wallets/:wallet_id/pooladdress", &controllers.OuterController{}, "GET:GetDepositWalletPoolAddress") 26 | beego.Router("/v1/mock/wallets/:wallet_id/pooladdress/balance", &controllers.OuterController{}, "GET:GetDepositWalletPoolAddressBalance") 27 | beego.Router("/v1/mock/wallets/:wallet_id/collection/notifications/manual", &controllers.OuterController{}, "POST:ResendDepositCollectionCallbacks") 28 | beego.Router("/v1/mock/wallets/:wallet_id/sender/transactions", &controllers.OuterController{}, "POST:WithdrawAssets") 29 | beego.Router("/v1/mock/wallets/:wallet_id/sender/transactions/:order_id/cancel", &controllers.OuterController{}, "POST:CancelWithdrawTransactions") 30 | beego.Router("/v1/mock/wallets/:wallet_id/sender/transactions/:order_id", &controllers.OuterController{}, "GET:GetWithdrawTransactionState") 31 | beego.Router("/v1/mock/wallets/:wallet_id/sender/transactions/:order_id/all", &controllers.OuterController{}, "GET:GetWithdrawTransactionStateAll") 32 | beego.Router("/v1/mock/wallets/:wallet_id/sender/transactions/eventlog", &controllers.OuterController{}, "GET:GetTransactionEventLog") 33 | beego.Router("/v1/mock/wallets/:wallet_id/sender/transactions", &controllers.OuterController{}, "GET:GetSenderTransactionHistory") 34 | beego.Router("/v1/mock/wallets/:wallet_id/sender/balance", &controllers.OuterController{}, "GET:GetWithdrawalWalletBalance") 35 | beego.Router("/v1/mock/wallets/:wallet_id/apisecret", &controllers.OuterController{}, "GET:GetTxAPITokenStatus") 36 | beego.Router("/v1/mock/wallets/:wallet_id/apisecret/activate", &controllers.OuterController{}, "POST:ActivateAPIToken") 37 | beego.Router("/v1/mock/wallets/:wallet_id/notifications", &controllers.OuterController{}, "GET:GetNotifications") 38 | beego.Router("/v1/mock/wallets/:wallet_id/notifications/get_by_id", &controllers.OuterController{}, "POST:GetCallbackBySerial") 39 | beego.Router("/v1/mock/wallets/:wallet_id/notifications/inspect", &controllers.OuterController{}, "POST:NotificationsInspect") 40 | beego.Router("/v1/mock/wallets/:wallet_id/receiver/notifications/txid/:txid/:vout_index", &controllers.OuterController{}, "GET:GetDepositCallback") 41 | beego.Router("/v1/mock/wallets/:wallet_id/sender/notifications/order_id/:order_id", &controllers.OuterController{}, "GET:GetWithdrawalCallback") 42 | beego.Router("/v1/mock/wallets/:wallet_id/transactions", &controllers.OuterController{}, "GET:GetTransactionHistory") 43 | beego.Router("/v1/mock/wallets/:wallet_id/blocks", &controllers.OuterController{}, "GET:GetWalletBlockInfo") 44 | beego.Router("/v1/mock/wallets/:wallet_id/addresses/invalid-deposit", &controllers.OuterController{}, "GET:GetInvalidDepositAddresses") 45 | beego.Router("/v1/mock/wallets/:wallet_id/info", &controllers.OuterController{}, "GET:GetWalletInfo") 46 | beego.Router("/v1/mock/wallets/:wallet_id/addresses/verify", &controllers.OuterController{}, "POST:VerifyAddresses") 47 | beego.Router("/v1/mock/wallets/:wallet_id/autofee", &controllers.OuterController{}, "POST:GetAutoFee") 48 | beego.Router("/v1/mock/wallets/:wallet_id/autofees", &controllers.OuterController{}, "POST:GetAutoFees") 49 | beego.Router("/v1/mock/wallets/:wallet_id/receiver/balance", &controllers.OuterController{}, "GET:GetDepositWalletBalance") 50 | beego.Router("/v1/mock/wallets/:wallet_id/vault/balance", &controllers.OuterController{}, "GET:GetVaultWalletBalance") 51 | beego.Router("/v1/mock/wallets/:wallet_id/addresses/contract_txid", &controllers.OuterController{}, "GET:GetDeployedContractCollectionAddresses") 52 | beego.Router("/v1/mock/wallets/:wallet_id/sender/transactions/acl", &controllers.OuterController{}, "POST:SetWithdrawalACL") 53 | beego.Router("/v1/mock/wallets/:wallet_id/sender/notifications/manual", &controllers.OuterController{}, "POST:ResendWithdrawalCallbacks") 54 | beego.Router("/v1/mock/wallets/:wallet_id/refreshsecret", &controllers.OuterController{}, "POST:RefreshSecret") 55 | beego.Router("/v1/mock/wallets/:wallet_id/sender/whitelist", &controllers.OuterController{}, "GET:GetSenderWhitelist") 56 | beego.Router("/v1/mock/wallets/:wallet_id/sender/whitelist", &controllers.OuterController{}, "POST:AddSenderWhitelist") 57 | beego.Router("/v1/mock/wallets/:wallet_id/sender/whitelist", &controllers.OuterController{}, "DELETE:RemoveSenderWhitelist") 58 | beego.Router("/v1/mock/wallets/:wallet_id/sender/whitelist/config", &controllers.OuterController{}, "GET:QuerySenderWhitelistConfig") 59 | beego.Router("/v1/mock/wallets/:wallet_id/sender/whitelist/check", &controllers.OuterController{}, "POST:CheckSenderWhitelist") 60 | beego.Router("/v1/mock/wallets/:wallet_id/signmessage", &controllers.OuterController{}, "POST:SignMessage") 61 | beego.Router("/v1/mock/wallets/:wallet_id/signtransaction", &controllers.OuterController{}, "POST:SignTransaction") 62 | beego.Router("/v1/mock/wallets/:wallet_id/contract/read", &controllers.OuterController{}, "GET:CallContractRead") 63 | beego.Router("/v1/mock/wallets/readonly/walletlist", &controllers.OuterController{}, "GET:GetReadOnlyWalletList") 64 | beego.Router("/v1/mock/wallets/readonly/walletlist/balances", &controllers.OuterController{}, "GET:GetReadOnlyWalletListBalances") 65 | beego.Router("/v1/mock/currency/prices", &controllers.OuterController{}, "GET:GetCurrencyPrices") 66 | beego.Router("/v1/mock/wallets/:wallet_id/receiver/get-balances", &controllers.OuterController{}, "POST:GetDelegatedBalances") 67 | beego.Router("/v1/mock/currency/:currency/contract/get-multiple-tokenuri", &controllers.OuterController{}, "POST:GetContractTokenMeta") 68 | beego.Router("/v1/mock/healthcheck", &controllers.OuterController{}, "GET:HealthCheck") 69 | 70 | beego.Router("/v1/mock/wallets/callback", &controllers.OuterController{}, "POST:Callback") 71 | beego.Router("/v1/mock/wallets/withdrawal/callback", &controllers.OuterController{}, "POST:WithdrawalCallback") 72 | 73 | beego.Router("/v1/mock/merchant/:merchant_id/apitoken", &controllers.MerchantController{}, "POST:SetAPIToken") 74 | beego.Router("/v1/mock/merchant/:merchant_id/order", &controllers.MerchantController{}, "POST:RequestPaymentOrder") 75 | beego.Router("/v1/mock/merchant/:merchant_id/order", &controllers.MerchantController{}, "GET:QueryPaymentOrder") 76 | beego.Router("/v1/mock/merchant/:merchant_id/order/duration", &controllers.MerchantController{}, "POST:UpdateOrderDuration") 77 | beego.Router("/v1/mock/merchant/:merchant_id/order", &controllers.MerchantController{}, "DELETE:CancelPaymentOrder") 78 | beego.Router("/v1/mock/merchant/:merchant_id/apisecret", &controllers.MerchantController{}, "GET:GetMerchantAPITokenStatus") 79 | beego.Router("/v1/mock/merchant/:merchant_id/apisecret/activate", &controllers.MerchantController{}, "POST:ActivateMerchantAPIToken") 80 | beego.Router("/v1/mock/merchant/:merchant_id/apisecret/refreshsecret", &controllers.MerchantController{}, "POST:RefreshMerchantSecret") 81 | beego.Router("/v1/mock/merchant/:merchant_id/notifications/manual", &controllers.MerchantController{}, "POST:ResendFailedMerchantCallbacks") 82 | 83 | beego.Router("/v1/mock/merchant/callback", &controllers.MerchantController{}, "POST:Callback") 84 | } 85 | -------------------------------------------------------------------------------- /controllers/MerchantController.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018-2022 The CYBAVO developers 2 | // All Rights Reserved. 3 | // NOTICE: All information contained herein is, and remains 4 | // the property of CYBAVO and its suppliers, 5 | // if any. The intellectual and technical concepts contained 6 | // herein are proprietary to CYBAVO 7 | // Dissemination of this information or reproduction of this materia 8 | // is strictly forbidden unless prior written permission is obtained 9 | // from CYBAVO. 10 | 11 | package controllers 12 | 13 | import ( 14 | "encoding/base64" 15 | "encoding/json" 16 | "errors" 17 | "fmt" 18 | "net/http" 19 | "net/url" 20 | "strconv" 21 | 22 | "github.com/astaxie/beego" 23 | "github.com/astaxie/beego/logs" 24 | "github.com/cybavo/SOFA_MOCK_SERVER/api" 25 | "github.com/cybavo/SOFA_MOCK_SERVER/models" 26 | ) 27 | 28 | type MerchantController struct { 29 | beego.Controller 30 | } 31 | 32 | func (c *MerchantController) getMerchantID() int64 { 33 | merchantID, err := strconv.ParseInt(c.Ctx.Input.Param(":merchant_id"), 10, 64) 34 | if err != nil { 35 | logs.Error("Invalid merchant ID =>", err) 36 | c.AbortWithError(http.StatusBadRequest, err) 37 | } 38 | return merchantID 39 | } 40 | 41 | func (c *MerchantController) AbortWithError(status int, err error) { 42 | resp := api.ErrorCodeResponse{ 43 | ErrMsg: err.Error(), 44 | ErrCode: status, 45 | } 46 | c.Data["json"] = resp 47 | c.Abort(strconv.Itoa(status)) 48 | } 49 | 50 | // @Title Set API token 51 | // @router /merchant/:merchant_id/apitoken [post] 52 | func (c *MerchantController) SetAPIToken() { 53 | defer c.ServeJSON() 54 | 55 | merchantID := c.getMerchantID() 56 | 57 | var request api.SetAPICodeRequest 58 | err := json.Unmarshal(c.Ctx.Input.RequestBody, &request) 59 | if err != nil { 60 | c.AbortWithError(http.StatusBadRequest, err) 61 | } 62 | 63 | apiCodeParams := models.APICode{ 64 | APICode: request.APICode, 65 | ApiSecret: request.ApiSecret, 66 | WalletID: merchantID, 67 | } 68 | err = models.SetAPICode(&apiCodeParams) 69 | if err != nil { 70 | logs.Error("SetAPICode failed", err) 71 | c.AbortWithError(http.StatusInternalServerError, err) 72 | } 73 | 74 | response := &api.CommonResponse{ 75 | Result: 1, 76 | } 77 | c.Data["json"] = response 78 | } 79 | 80 | // @Title Request a payment order 81 | // @router /merchant/:merchant_id/order [post] 82 | func (c *MerchantController) RequestPaymentOrder() { 83 | defer c.ServeJSON() 84 | 85 | req := api.RequestPaymentOrderRequest{} 86 | if err := json.Unmarshal([]byte(c.Ctx.Input.RequestBody), &req); err != nil { 87 | c.AbortWithError(http.StatusInternalServerError, err) 88 | } 89 | 90 | request := c.Ctx.Input.RequestBody 91 | if len(req.RedirectURL) > 0 { 92 | req.RedirectURL = url.QueryEscape(req.RedirectURL) 93 | } else { 94 | request, _ = json.Marshal(req) 95 | } 96 | 97 | merchantID := c.getMerchantID() 98 | resp, err := api.MakeRequest(merchantID, "POST", fmt.Sprintf("/v1/merchant/%d/order", merchantID), 99 | nil, request) 100 | if err != nil { 101 | logs.Error("RequestPaymentOrder failed", err) 102 | c.AbortWithError(http.StatusInternalServerError, err) 103 | } 104 | 105 | var m map[string]interface{} 106 | json.Unmarshal(resp, &m) 107 | c.Data["json"] = m 108 | } 109 | 110 | // @Title Query certain payment order 111 | // @router /merchant/:merchant_id/order [get] 112 | func (c *MerchantController) QueryPaymentOrder() { 113 | defer c.ServeJSON() 114 | 115 | merchantID := c.getMerchantID() 116 | resp, err := api.MakeRequest(merchantID, "GET", fmt.Sprintf("/v1/merchant/%d/order", merchantID), 117 | getQueryString(c.Ctx), nil) 118 | if err != nil { 119 | logs.Error("QueryPaymentOrder failed", err) 120 | c.AbortWithError(http.StatusInternalServerError, err) 121 | } 122 | 123 | res := api.QueryPaymentOrderResponse{} 124 | if err := json.Unmarshal(resp, &res); err != nil { 125 | c.AbortWithError(http.StatusInternalServerError, err) 126 | } 127 | 128 | if len(res.RedirectURL) > 0 { 129 | res.RedirectURL, _ = url.QueryUnescape(res.RedirectURL) 130 | resp, _ = json.Marshal(res) 131 | } 132 | 133 | var m map[string]interface{} 134 | json.Unmarshal(resp, &m) 135 | c.Data["json"] = m 136 | } 137 | 138 | // @Title Update duration of certain payment order 139 | // @router /merchant/:merchant_id/order/duration [post] 140 | func (c *MerchantController) UpdateOrderDuration() { 141 | defer c.ServeJSON() 142 | 143 | merchantID := c.getMerchantID() 144 | resp, err := api.MakeRequest(merchantID, "POST", fmt.Sprintf("/v1/merchant/%d/order/duration", merchantID), 145 | nil, c.Ctx.Input.RequestBody) 146 | if err != nil { 147 | logs.Error("UpdateOrderDuration failed", err) 148 | c.AbortWithError(http.StatusInternalServerError, err) 149 | } 150 | 151 | var m map[string]interface{} 152 | json.Unmarshal(resp, &m) 153 | c.Data["json"] = m 154 | } 155 | 156 | // @Title Update duration of certain payment order 157 | // @router /merchant/:merchant_id/order [delete] 158 | func (c *MerchantController) CancelPaymentOrder() { 159 | defer c.ServeJSON() 160 | 161 | merchantID := c.getMerchantID() 162 | resp, err := api.MakeRequest(merchantID, "DELETE", fmt.Sprintf("/v1/merchant/%d/order", merchantID), 163 | getQueryString(c.Ctx), nil) 164 | if err != nil { 165 | logs.Error("CancelPaymentOrder failed", err) 166 | c.AbortWithError(http.StatusInternalServerError, err) 167 | } 168 | 169 | var m map[string]interface{} 170 | json.Unmarshal(resp, &m) 171 | c.Data["json"] = m 172 | } 173 | 174 | // @Title Get merchant API token status 175 | // @router /merchant/:merchant_id/apisecret [get] 176 | func (c *MerchantController) GetMerchantAPITokenStatus() { 177 | defer c.ServeJSON() 178 | 179 | merchantID := c.getMerchantID() 180 | resp, err := api.MakeRequest(merchantID, "GET", fmt.Sprintf("/v1/merchant/%d/apisecret", merchantID), 181 | nil, nil) 182 | if err != nil { 183 | logs.Error("GetMerchantAPITokenStatus failed", err) 184 | c.AbortWithError(http.StatusInternalServerError, err) 185 | } 186 | 187 | var m map[string]interface{} 188 | json.Unmarshal(resp, &m) 189 | c.Data["json"] = m 190 | } 191 | 192 | // @Title Activate merchant API token 193 | // @router /merchant/:merchant_id/apisecret/activate [post] 194 | func (c *MerchantController) ActivateMerchantAPIToken() { 195 | defer c.ServeJSON() 196 | 197 | merchantID := c.getMerchantID() 198 | resp, err := api.MakeRequest(merchantID, "POST", fmt.Sprintf("/v1/merchant/%d/apisecret/activate", merchantID), 199 | nil, nil) 200 | if err != nil { 201 | logs.Error("ActivateMerchantAPIToken failed", err) 202 | c.AbortWithError(http.StatusInternalServerError, err) 203 | } 204 | 205 | var m map[string]interface{} 206 | json.Unmarshal(resp, &m) 207 | c.Data["json"] = m 208 | } 209 | 210 | // @Title Refresh merchant API code and secret 211 | // @router /merchant/:merchant_id/apisecret/refreshsecret [post] 212 | func (c *MerchantController) RefreshMerchantSecret() { 213 | defer c.ServeJSON() 214 | 215 | merchantID := c.getMerchantID() 216 | resp, err := api.MakeRequest(merchantID, "POST", fmt.Sprintf("/v1/merchant/%d/apisecret/refreshsecret", merchantID), 217 | nil, c.Ctx.Input.RequestBody) 218 | if err != nil { 219 | logs.Error("RefreshMerchantSecret failed", err) 220 | c.AbortWithError(http.StatusInternalServerError, err) 221 | } 222 | 223 | var m map[string]interface{} 224 | json.Unmarshal(resp, &m) 225 | c.Data["json"] = m 226 | } 227 | 228 | // @Title Resend failed callback 229 | // @router /merchant/:merchant_id/notifications/manual [post] 230 | func (c *MerchantController) ResendFailedMerchantCallbacks() { 231 | defer c.ServeJSON() 232 | 233 | merchantID := c.getMerchantID() 234 | resp, err := api.MakeRequest(merchantID, "POST", fmt.Sprintf("/v1/merchant/%d/notifications/manual", merchantID), 235 | nil, nil) 236 | if err != nil { 237 | logs.Error("ResendFailedMerchantCallbacks failed", err) 238 | c.AbortWithError(http.StatusInternalServerError, err) 239 | } 240 | 241 | var m map[string]interface{} 242 | json.Unmarshal(resp, &m) 243 | c.Data["json"] = m 244 | } 245 | 246 | // @Title Merchant callback 247 | // @router /merchant/callback [post] 248 | func (c *MerchantController) Callback() { 249 | var cb api.MerchantCallbackStruct 250 | err := json.Unmarshal(c.Ctx.Input.RequestBody, &cb) 251 | if err != nil { 252 | c.AbortWithError(http.StatusBadRequest, err) 253 | } 254 | 255 | apiCodeObj, err := models.GetWalletAPICode(cb.MerchantID) 256 | if err != nil { 257 | c.AbortWithError(http.StatusBadRequest, err) 258 | } 259 | 260 | checksum := c.Ctx.Input.Header("X-CHECKSUM") 261 | payload := string(c.Ctx.Input.RequestBody) + apiCodeObj.ApiSecret 262 | sha, _ := api.CalcSHA256([]byte(payload)) 263 | checksumVerf := base64.URLEncoding.EncodeToString(sha) 264 | 265 | if checksum != checksumVerf { 266 | c.AbortWithError(http.StatusBadRequest, errors.New("Bad checksum")) 267 | } 268 | 269 | logs.Debug("Merchant Callback => %s", c.Ctx.Input.RequestBody) 270 | 271 | if cb.State == api.MerchantOrderStateSuccess { 272 | } else if cb.State == api.MerchantOrderStateExpired { 273 | } else if cb.State == api.MerchantOrderStateInsufficient { 274 | } else if cb.State == api.MerchantOrderStateExcess { 275 | } else if cb.State == api.MerchantOrderStateCancel { 276 | } 277 | 278 | // reply 200 OK to confirm the callback has been processed 279 | c.Ctx.WriteString("OK") 280 | } 281 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /merchant/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Table of contents 3 | 4 | - [Get Started](#get-started) 5 | - [API Authentication](#api-authentication) 6 | - [Callback Integration](#callback-integration) 7 | - REST API 8 | - [Create Payment Order](#create-payment-order) 9 | - [Query Payment Order Status](#query-payment-order-status) 10 | - [Cancel Payment Order](#cancel-payment-order) 11 | - [Update Payment Order Expiration Duration](#update-payment-order-expiration-duration) 12 | - [Activate Merchant API Code](#activate-merchant-api-code) 13 | - [Query Merchant API Code Status](#query-merchant-api-code-status) 14 | - [Refresh Merchant API Code](#refresh-merchant-api-code) 15 | - Testing 16 | - [Mock Server](#mock-server) 17 | - [cURL Testing Commands](#curl-testing-commands) 18 | - [Other Language Versions](#other-language-versions) 19 | - Appendix 20 | - [Callback Definition](#callback-definition) 21 | - [Currency Definition](https://github.com/CYBAVO/SOFA_MOCK_SERVER#currency-definition) 22 | 23 | 24 | # Get Started 25 | 26 | CYBAVO Merchant Service(CMS) is a comprehensive cryptocurrency payment servcie, based on the deposit wallet of CYBAVO VAULT. 27 | 28 | ### Try it now 29 | - Use [mock server](#mock-server) to test CYBAVO Merchant API right away. 30 | 31 | ### Start integration 32 | - To make a correct API call, refer to [API Authentication](#api-authentication). 33 | - To handle callback correctly, refer to [Callback Integration](#callback-integration). 34 | 35 | 36 | # API Authentication 37 | 38 | - The CYBAVO Merchant Service verifies all incoming requests. All requests must include X-API-CODE, X-CHECKSUM headers otherwise caller will get a 403 Forbidden error. 39 | 40 | ### How to acquire and refresh API code and secret 41 | - Request the merchant API code/secret from the **Merchant Details** page on the web control panel for the first time. 42 | - A paired refresh code can be used in the [refresh API](#refresh-merchant-api-code) to acquire the new inactive API code/secret of the wallet. 43 | - Before the inactive API code is activated, the currently activated API code is still valid. 44 | - Once the paired API code becomes invalid, the paired refresh code will also become invalid. 45 | 46 | ### How to make a correct request? 47 | - Put the merchant API code in the X-API-CODE header. 48 | - Use the inactivated API code in any request will activate it automatically. Once activated, the currently activated API code will immediately become invalid. 49 | - Or you can explicitly call the [activation API](#activate-merchant-api-code) to activate the API code before use 50 | - Calculate the checksum with the corresponding API secret and put the checksum in the X-CHECKSUM header. 51 | - The checksum calculation will use all the query parameters, the current timestamp, user-defined random string and the post body (if any). 52 | - Please refer to the code snippet on the github project to know how to calculate the checksum. 53 | - [Go](https://github.com/CYBAVO/SOFA_MOCK_SERVER/blob/master/api/apicaller.go#L40) 54 | - [Java](https://github.com/CYBAVO/SOFA_MOCK_SERVER_JAVA/blob/master/src/main/java/com/cybavo/sofa/mock/Api.java#L71) 55 | - [Javascript](https://github.com/CYBAVO/SOFA_MOCK_SERVER_JAVASCRIPT/blob/master/helper/apicaller.js#L58) 56 | - [PHP](https://github.com/CYBAVO/SOFA_MOCK_SERVER_PHP/blob/master/helper/apicaller.php#L26) 57 | 58 | 59 | 60 | # Callback Integration 61 | 62 | - Please note that the merchant must have an activated API code, otherwise no callback will be sent. 63 | - Use the [activation API](#activate-merchant-api-code) to activate an API code. 64 | 65 | - To ensure that the callbacks have processed by callback handler, the CYBAVO Merchant Service will continue to send the callbacks to the callback URL until a callback confirmation (HTTP/1.1 200 OK) is received or exceeds the number of retries (retry time interval: 1-3-5-15-45 mins). 66 | - If all attempts fail, the callback will be set to a failed state, the callback handler can call the [resend failed callbacks](#resend-failed-merchant-callbacks) API to request CYBAVO Merchant Serviceto resend such kind of callback(s) or through the web control panel. 67 | 68 | - Refer to [Callback Definition](#callback-definition), [Callback Type Definition](#callback-type-definition) for detailed definition. 69 | - Please refer to the code snippet on the github project to know how to validate the callback payload. 70 | - [Go](https://github.com/CYBAVO/SOFA_MOCK_SERVER/blob/master/controllers/MerchantController.go#L248) 71 | - [Java](https://github.com/CYBAVO/SOFA_MOCK_SERVER_JAVA/blob/master/src/main/java/com/cybavo/sofa/mock/MerchantController.java#L165) 72 | - [Javascript](https://github.com/CYBAVO/SOFA_MOCK_SERVER_JAVASCRIPT/blob/master/routes/merchant.js#L163) 73 | - [PHP](https://github.com/CYBAVO/SOFA_MOCK_SERVER_PHP/blob/master/index.php#L418) 74 | 75 | # REST API 76 | 77 | 78 | ### Create Payment Order 79 | 80 | Create a payment order. 81 | 82 | ##### Request 83 | 84 | **POST** /v1/merchant/`MERCHANT_ID`/order 85 | 86 | - [Sample curl command](#curl-create-payment-order) 87 | 88 | > The order\_id must be prefixed. **Find prefix from corresponding merchant detail on web control panel.** 89 | > 90 | > The prefix is `N520335069_` is the following example. 91 | 92 | ##### Request Format 93 | 94 | An example of the request: 95 | 96 | ###### API 97 | 98 | ``` 99 | /v1/merchant/520335069/order 100 | ``` 101 | 102 | ###### Post body 103 | 104 | ```json 105 | { 106 | "currency": 60, 107 | "token_address": "", 108 | "amount": "0.01", 109 | "duration": 50, 110 | "description": "ETH payment", 111 | "order_id": "N520335069_1000022", 112 | "redirect_url": "https%3A%2F%2Fmyredirect.example.com%3Fk%3Dv%26k1%3Dv1" 113 | } 114 | ``` 115 | 116 | The request includes the following parameters: 117 | 118 | ###### Post body 119 | 120 | | Field | Type | Note | Description | 121 | | :--- | :--- | :--- | :--- | 122 | | currency | int64 | required, refer to the [Currency Definition](#currency-definition) | The cryptocurrency used to pay the order | 123 | | token_address | string | optional | The token contract address of cryptocurrency used to pay the order | 124 | | amount | string | required, refer to [the definition](#doundary-definition) for the default maximum amount | The required amount of the payment order | 125 | | duration | int64 | optional, refer to [the definition](#doundary-definition) for the minimum duration | The expiration duration (in minutes) of the payment order | 126 | | description | string | optional, max `255` chars | The description of the payment order | 127 | | order_id | string | required | The user defined order ID (must be prefixed) | 128 | | redirect_url | string | optional | User defined redirect URL (must be encoded) | 129 | 130 | > The `redirect_url` must be encoded. Please refer to the code snippet on the github project to know how to encode the URL. [Go](https://github.com/CYBAVO/SOFA_MOCK_SERVER/blob/master/controllers/MerchantController.go#L92), [Java](https://github.com/CYBAVO/SOFA_MOCK_SERVER_JAVA/blob/master/src/main/java/com/cybavo/sofa/mock/MerchantController.java#L72), [Javascript](https://github.com/CYBAVO/SOFA_MOCK_SERVER_JAVASCRIPT/blob/master/routes/merchant.js#L45), [PHP](https://github.com/CYBAVO/SOFA_MOCK_SERVER_PHP/blob/master/index.php#L370) 131 | > 132 | > If using BNB or XLM payment order, the payment transaction must specify an accurate memo to complete the order. 133 | 134 | 135 | Boundary definition 136 | 137 | | ID | Currency Symbol | Minimum Duration | Default Duration | Default Minimum / Maximum Amount | Confirm Blocks | 138 | | :--- | :--- | :--- | :--- | :--- | :--- | 139 | | 0 | BTC | 20 | 120 | 0.00002 / 0.2 | 2 | 140 | | 2 | LTC | 20 | 120 | 0.005 / 50 | 6 | 141 | | 3 | DOGE | 20 | 120 | 0.005 / 50 | 15 | 142 | | 60 | ETH | 10 | 60 | 0.0005 / 5 | 6 | 143 | | 145 | BCH | 20 | 120 | 0.005 / 20 | 6 | 144 | | 148 | XLM | 10 | 30 | 2 / 2000 | 1 | 145 | | 195 | TRX | 10 | 30 | 10 / 10000 | 1 | 146 | | 714 | BNB | 10 | 30 | 0.003 / 30 | 1 | 147 | | 966 | MATIC | 10 | 60 | 0.005 / 50 | 50 | 148 | | 1815 | ADA | 10 | 30 | 1 / 1000 | 30 | 149 | | 99999999997 | BSC | 10 | 60 | 0.005 / 50 | 20 | 150 | 151 | > The `Maximum Amount` boundary can be adjusted in the web control panel. 152 | 153 | ##### Response Format 154 | 155 | An example of a successful response: 156 | 157 | ```json 158 | { 159 | "access_token": "ybJWKM_CT1yXxzLO2z1Y5fg1EzHuMyRA14ubzR8i-RE", 160 | "address": "0xed965D0A23eC4583f55Fb5d4109C0fE069B396fC", 161 | "expired_time": 1615975467, 162 | "order_id": "N520335069_1000022" 163 | } 164 | ``` 165 | 166 | For BNB, XLM 167 | 168 | ```json 169 | { 170 | "access_token": "ybJWKM_CT1yXxzLO2z1Y5fg1EzHuMyRA14ubzR8i-RE", 171 | "address": "0xed965D0A23eC4583f55Fb5d4109C0fE069B396fC", 172 | "expired_time": 1615975467, 173 | "order_id": "N520335069_1000023", 174 | "memo": "63574" 175 | } 176 | ``` 177 | 178 | The response includes the following parameters: 179 | 180 | | Field | Type | Description | 181 | | :--- | :--- | :--- | 182 | | access_token | string | The access token used to query, update or cancel the payment order | 183 | | address | string | The address to accept the payment | 184 | | expired_time | int64 | The due date of the payment order (unix time in UTC) | 185 | | order_id | string | The order ID of the payment order | 186 | | memo | string | The memo of the payment order, the payment transaction must specify an accurate memo to complete the order. | 187 | 188 | ##### Error Code 189 | 190 | | HTTP Code | Error Code | Error | Message | Description | 191 | | :--- | :--- | :--- | :--- | :--- | 192 | | 403 | - | Forbidden. Invalid ID | - | No merchant ID found | 193 | | 403 | - | Forbidden. Header not found | - | Missing `X-API-CODE`, `X-CHECKSUM` header or query param `t` | 194 | | 403 | - | Forbidden. Invalid timestamp | - | The timestamp `t` is not in the valid time range | 195 | | 403 | - | Forbidden. Invalid checksum | - | The request is considered a replayed request | 196 | | 403 | - | Forbidden. Invalid API code | - | `X-API-CODE` header contains invalid API code | 197 | | 403 | - | Forbidden. Checksum unmatch | - | `X-CHECKSUM` header contains wrong checksum | 198 | | 403 | - | Forbidden. Call too frequently ({THROTTLING_COUNT} calls/minute) | - | Send requests too frequently | 199 | | 400 | 112 | Invalid parameter | - | Malformatted post body | 200 | | 403 | 113 | Permission denied | - | Merchant API not allowed | 201 | | 400 | 20003 | Wallet address not available | - | There is no available deposit address | 202 | | 400 | 20007 | Wallet not found | - | No corresponding linked deposit wallet found (specified by the currency and token address in the request) | 203 | | 400 | 20009 | Duplicated Order ID| - | The order ID has been used | 204 | | 400 | 20010 | Wrong Order prefix | - | The order prefix is wrong | 205 | | 400 | 20011 | Wrong Order format | - | The order contains invalid characters | 206 | | 400 | 20014 | Wrong order amount | - | The decimals of the amount does not conform to currency's decimals | 207 | | 400 | 20015 | Invalid order duration, should not be less than minimum | - | The requested duration is less than the lower bound | 208 | | 400 | 20016 | Over amount limit| - | The requested amount exceeds upper bound | 209 | | 400 | 20017 | Invalid url host | - | The redirect URL is invalid | 210 | 211 | ##### [Back to top](#table-of-contents) 212 | 213 | 214 | 215 | ### Query Payment Order Status 216 | 217 | Query current status of a payment order. 218 | 219 | ##### Request 220 | 221 | `VIEW` **GET** /v1/merchant/`MERCHANT_ID`/order?token=`ACCESS_TOKEN`&order=`ORDER_ID` 222 | 223 | - [Sample curl command](#curl-query-payment-order-status) 224 | 225 | ##### Request Format 226 | 227 | An example of the request: 228 | 229 | ###### API 230 | 231 | ``` 232 | /v1/merchant/520335069/order?token=ybJWKM_CT1yXxzLO2z1Y5fg1EzHuMyRA14ubzR8i-RE&order=N520335069_1000022 233 | ``` 234 | 235 | ##### Response Format 236 | 237 | An example of a successful response: 238 | 239 | ```json 240 | { 241 | "address": "0xed965D0A23eC4583f55Fb5d4109C0fE069B396fC", 242 | "expired_time": 1615975468, 243 | "redirect_url": "https%3A%2F%2Fmyredirect.example.com%3Fk%3Dv%26k1%3Dv1", 244 | "state": 1, 245 | "tx_id": "" 246 | } 247 | ``` 248 | 249 | The response includes the following parameters: 250 | 251 | | Field | Type | Description | 252 | | :--- | :--- | :--- | 253 | | address | string | The deposit address to accept payment | 254 | | expired_time | int64 | The due date of the payment (unix time in UTC) | 255 | | redirect_url | string | User defined redirect URL (encoded) | 256 | | state | int | Refer to [Order State Definition](#order-state-definition) | 257 | | tx_id | string | The TX ID of the payment if state is 0, 2 or 3 | 258 | 259 | > The `redirect_url` is encoded. Please refer to the code snippet on the github project to know how to decode the URL. [Go](https://github.com/CYBAVO/SOFA_MOCK_SERVER/blob/master/controllers/MerchantController.go#L129), [Java](https://github.com/CYBAVO/SOFA_MOCK_SERVER_JAVA/blob/master/src/main/java/com/cybavo/sofa/mock/MerchantController.java#L97), [Javascript](https://github.com/CYBAVO/SOFA_MOCK_SERVER_JAVASCRIPT/blob/master/routes/merchant.js#L66), [PHP](https://github.com/CYBAVO/SOFA_MOCK_SERVER_PHP/blob/master/index.php#L381) 260 | 261 | ##### Error Code 262 | 263 | | HTTP Code | Error Code | Error | Message | Description | 264 | | :--- | :--- | :--- | :--- | :--- | 265 | | 403 | - | Forbidden. Invalid ID | - | No merchant ID found | 266 | | 403 | - | Forbidden. Header not found | - | Missing `X-API-CODE`, `X-CHECKSUM` header or query param `t` | 267 | | 403 | - | Forbidden. Invalid timestamp | - | The timestamp `t` is not in the valid time range | 268 | | 403 | - | Forbidden. Invalid checksum | - | The request is considered a replayed request | 269 | | 403 | - | Forbidden. Invalid API code | - | `X-API-CODE` header contains invalid API code | 270 | | 403 | - | Forbidden. Checksum unmatch | - | `X-CHECKSUM` header contains wrong checksum | 271 | | 403 | - | Forbidden. Call too frequently ({THROTTLING_COUNT} calls/minute) | - | Send requests too frequently | 272 | | 403 | 112 | Invalid parameter | - | The invalid ACCESS\_TOKEN/ORDER\_ID | 273 | | 403 | 113 | Permission denied | - | Merchant API not allowed | 274 | 275 | ##### [Back to top](#table-of-contents) 276 | 277 | 278 | 279 | ### Cancel Payment Order 280 | 281 | Cancel a payment order. Only pending payment order can be cancelled. 282 | 283 | ##### Request 284 | 285 | **DELETE** /v1/merchant/`MERCHANT_ID`/order?token=`ACCESS_TOKEN`&order=`ORDER_ID` 286 | 287 | - [Sample curl command](#curl-cancel-payment-order) 288 | 289 | ##### Request Format 290 | 291 | An example of the request: 292 | 293 | ###### API 294 | 295 | ``` 296 | /v1/merchant/520335069/order?token=ybJWKM_CT1yXxzLO2z1Y5fg1EzHuMyRA14ubzR8i-RE&order=N520335069_1000022 297 | ``` 298 | 299 | ##### Response Format 300 | 301 | An example of a successful response: 302 | 303 | ```json 304 | { 305 | "result": 1 306 | } 307 | ``` 308 | 309 | The response includes the following parameters: 310 | 311 | | Field | Type | Description | 312 | | :--- | :--- | :--- | 313 | | result | int | Always be 1, means the payment order has been successfully cancelled | 314 | 315 | ##### Error Code 316 | 317 | | HTTP Code | Error Code | Error | Message | Description | 318 | | :--- | :--- | :--- | :--- | :--- | 319 | | 403 | - | Forbidden. Invalid ID | - | No merchant ID found | 320 | | 403 | - | Forbidden. Header not found | - | Missing `X-API-CODE`, `X-CHECKSUM` header or query param `t` | 321 | | 403 | - | Forbidden. Invalid timestamp | - | The timestamp `t` is not in the valid time range | 322 | | 403 | - | Forbidden. Invalid checksum | - | The request is considered a replayed request | 323 | | 403 | - | Forbidden. Invalid API code | - | `X-API-CODE` header contains invalid API code | 324 | | 403 | - | Forbidden. Checksum unmatch | - | `X-CHECKSUM` header contains wrong checksum | 325 | | 403 | - | Forbidden. Call too frequently ({THROTTLING_COUNT} calls/minute) | - | Send requests too frequently | 326 | | 403 | 112 | Invalid parameter | - | The invalid ACCESS\_TOKEN/ORDER\_ID or the payment can't be cancelled | 327 | | 403 | 113 | Permission denied | - | Merchant API not allowed | 328 | 329 | 330 | ##### [Back to top](#table-of-contents) 331 | 332 | 333 | 334 | ### Update Payment Order Expiration Duration 335 | 336 | Update a payment order expiration duration. Only pending payment order can be updated. 337 | 338 | **POST** /v1/merchant/`MERCHANT_ID`/order/duration 339 | 340 | - [Sample curl command](#curl-update-payment-order-expiration-duration) 341 | 342 | ##### Request Format 343 | 344 | An example of the request: 345 | 346 | ###### API 347 | 348 | ``` 349 | /v1/merchant/520335069/order 350 | ``` 351 | 352 | ###### Post body 353 | 354 | ```json 355 | { 356 | "access_token": "ybJWKM_CT1yXxzLO2z1Y5fg1EzHuMyRA14ubzR8i-RE", 357 | "order_id": "N520335069_1000022", 358 | "duration": 50 359 | } 360 | ``` 361 | 362 | The request includes the following parameters: 363 | 364 | ###### Post body 365 | 366 | | Field | Type | Note | Description | 367 | | :--- | :--- | :--- | :--- | 368 | | access_token | string | required | The access token of the payment order returned with [Create order](#create-payment-order) API | 369 | | order_id | string | required | The order ID of the payment order | 370 | | duration | int64 | required | The expiration duration (in minutes) of the payment order | 371 | 372 | > The new due date is calculated based on the submission time of the payment order. 373 | 374 | ##### Response Format 375 | 376 | An example of a successful response: 377 | 378 | ```json 379 | { 380 | "result": 1 381 | } 382 | ``` 383 | 384 | The response includes the following parameters: 385 | 386 | | Field | Type | Description | 387 | | :--- | :--- | :--- | 388 | | result | int | Always be 1, means the payment order has been successfully updated | 389 | 390 | ##### Error Code 391 | 392 | | HTTP Code | Error Code | Error | Message | Description | 393 | | :--- | :--- | :--- | :--- | :--- | 394 | | 403 | - | Forbidden. Invalid ID | - | No merchant ID found | 395 | | 403 | - | Forbidden. Header not found | - | Missing `X-API-CODE`, `X-CHECKSUM` header or query param `t` | 396 | | 403 | - | Forbidden. Invalid timestamp | - | The timestamp `t` is not in the valid time range | 397 | | 403 | - | Forbidden. Invalid checksum | - | The request is considered a replayed request | 398 | | 403 | - | Forbidden. Invalid API code | - | `X-API-CODE` header contains invalid API code | 399 | | 403 | - | Forbidden. Checksum unmatch | - | `X-CHECKSUM` header contains wrong checksum | 400 | | 403 | - | Forbidden. Call too frequently ({THROTTLING_COUNT} calls/minute) | - | Send requests too frequently | 401 | | 403 | 112 | Invalid parameter | - | The invalid ACCESS\_TOKEN/ORDER\_ID or the payment can't be updated | 402 | | 403 | 113 | Permission denied | - | Merchant API not allowed | 403 | 404 | ##### [Back to top](#table-of-contents) 405 | 406 | 407 | 408 | ### Resend Failed Merchant Callbacks 409 | 410 | The callback handler can call this API to resend pending or failed merchant callbacks. 411 | 412 | Refer to [Callback Integration](#callback-integration) for callback rules. 413 | 414 | > The resend operation could be requested on the web control panel as well. 415 | 416 | ##### Request 417 | 418 | **POST** /v1/merchant/`MERCHANT_ID`/notifications/manual 419 | 420 | - [Sample curl command](#curl-resend-failed-merchant-callbacks) 421 | 422 | ##### Request Format 423 | 424 | An example of the request: 425 | 426 | ###### API 427 | 428 | ``` 429 | /v1/merchant/520335069/notifications/manual 430 | ``` 431 | 432 | ##### Response Format 433 | 434 | An example of a successful response: 435 | 436 | ```json 437 | { 438 | "count": 0 439 | } 440 | ``` 441 | 442 | The response includes the following parameters: 443 | 444 | | Field | Type | Description | 445 | | :--- | :--- | :--- | 446 | | count | int | Count of callbacks just resent | 447 | 448 | ##### Error Code 449 | 450 | | HTTP Code | Error Code | Error | Message | Description | 451 | | :--- | :--- | :--- | :--- | :--- | 452 | | 403 | - | Forbidden. Invalid ID | - | No merchant ID found | 453 | | 403 | - | Forbidden. Header not found | - | Missing `X-API-CODE`, `X-CHECKSUM` header or query param `t` | 454 | | 403 | - | Forbidden. Invalid timestamp | - | The timestamp `t` is not in the valid time range | 455 | | 403 | - | Forbidden. Invalid checksum | - | The request is considered a replayed request | 456 | | 403 | - | Forbidden. Invalid API code | - | `X-API-CODE` header contains invalid API code | 457 | | 403 | - | Forbidden. Checksum unmatch | - | `X-CHECKSUM` header contains wrong checksum | 458 | | 403 | - | Forbidden. Call too frequently ({THROTTLING_COUNT} calls/minute) | - | Send requests too frequently | 459 | | 403 | 113 | Permission denied | - | Merchant API not allowed | 460 | 461 | ##### [Back to top](#table-of-contents) 462 | 463 | 464 | 465 | ### Activate Merchant API Code 466 | 467 | Activate the API code of a certain merchant. Once activated, the currently activated API code will immediately become invalid. 468 | 469 | ##### Request 470 | 471 | **POST** /v1/merchant/`MERCHANT_ID`/apisecret/activate 472 | 473 | - [Sample curl command](#curl-activate-merchant-api-code) 474 | 475 | ##### Request Format 476 | 477 | An example of the request: 478 | 479 | ###### API 480 | 481 | ``` 482 | /v1/merchant/520335069/apisecret/activate 483 | ``` 484 | 485 | ##### Response Format 486 | 487 | An example of a successful response: 488 | 489 | ```json 490 | { 491 | "api_code": "4PcdE9VjXfrk7WjC1", 492 | "exp": 1609646716 493 | } 494 | ``` 495 | 496 | The response includes the following parameters: 497 | 498 | | Field | Type | Description | 499 | | :--- | :--- | :--- | 500 | | api_code | string | The activated API code | 501 | | exp | int64 | The API code expiration unix time in UTC | 502 | 503 | ##### Error Code 504 | 505 | | HTTP Code | Error Code | Error | Message | Description | 506 | | :--- | :--- | :--- | :--- | :--- | 507 | | 403 | - | Forbidden. Invalid ID | - | No merchant ID found | 508 | | 403 | - | Forbidden. Header not found | - | Missing `X-API-CODE`, `X-CHECKSUM` header or query param `t` | 509 | | 403 | - | Forbidden. Invalid timestamp | - | The timestamp `t` is not in the valid time range | 510 | | 403 | - | Forbidden. Invalid checksum | - | The request is considered a replayed request | 511 | | 403 | - | Forbidden. Invalid API code | - | `X-API-CODE` header contains invalid API code | 512 | | 403 | - | Forbidden. Checksum unmatch | - | `X-CHECKSUM` header contains wrong checksum | 513 | | 403 | - | Forbidden. Call too frequently ({THROTTLING_COUNT} calls/minute) | - | Send requests too frequently | 514 | | 403 | 113 | Permission denied | - | Merchant API not allowed | 515 | 516 | ##### [Back to top](#table-of-contents) 517 | 518 | 519 | 520 | ### Query Merchant API Code Status 521 | 522 | Query the API code info of a certain merchant. Use the `inactivated` API code in any request will activate it. Once activated, the currently activated API code will immediately become invalid. 523 | 524 | ##### Request 525 | 526 | `VIEW` **GET** /v1/merchant/`MERCHANT_ID`/apisecret 527 | 528 | - [Sample curl command](#curl-query-merchant-api-code-status) 529 | 530 | ##### Request Format 531 | 532 | An example of the request: 533 | 534 | ###### API 535 | 536 | ``` 537 | /v1/merchant/520335069/apisecret 538 | ``` 539 | 540 | ##### Response Format 541 | 542 | An example of a successful response: 543 | 544 | ```json 545 | { 546 | "valid": { 547 | "api_code": "H4Q6xFZgiTZb37GN", 548 | "exp": 1583144863 549 | }, 550 | "inactivated": { 551 | "api_code": "32PmGCjNzXda4mNHX" 552 | } 553 | } 554 | ``` 555 | 556 | The response includes the following parameters: 557 | 558 | | Field | Type | Description | 559 | | :--- | :--- | :--- | 560 | | valid | object | The activated API code | 561 | | inactivated | object | Not active API code | 562 | | api_code | string | The API code for querying wallet | 563 | | exp | int64 | The API code expiration unix time in UTC | 564 | 565 | > Use an invalid API-CODE, the caller will get a 403 Forbidden error. 566 | 567 | ##### Error Code 568 | 569 | | HTTP Code | Error Code | Error | Message | Description | 570 | | :--- | :--- | :--- | :--- | :--- | 571 | | 403 | - | Forbidden. Invalid ID | - | No merchant ID found | 572 | | 403 | - | Forbidden. Header not found | - | Missing `X-API-CODE`, `X-CHECKSUM` header or query param `t` | 573 | | 403 | - | Forbidden. Invalid timestamp | - | The timestamp `t` is not in the valid time range | 574 | | 403 | - | Forbidden. Invalid checksum | - | The request is considered a replayed request | 575 | | 403 | - | Forbidden. Invalid API code | - | `X-API-CODE` header contains invalid API code | 576 | | 403 | - | Forbidden. Checksum unmatch | - | `X-CHECKSUM` header contains wrong checksum | 577 | | 403 | - | Forbidden. Call too frequently ({THROTTLING_COUNT} calls/minute) | - | Send requests too frequently | 578 | | 403 | 113 | Permission denied | - | Merchant API not allowed | 579 | 580 | ##### [Back to top](#table-of-contents) 581 | 582 | 583 | 584 | ### Refresh Merchant API Code 585 | 586 | Use paired refresh code to acquire the new inactive API code/secret of the merchant. 587 | 588 | ##### Request 589 | 590 | **POST** /v1/merchant/`MERCHANT_ID`/apisecret/refreshsecret 591 | 592 | - [Sample curl command](#curl-refresh-merchant-api-code) 593 | 594 | ##### Request Format 595 | 596 | An example of the request: 597 | 598 | ###### API 599 | 600 | ``` 601 | /v1/merchant/520335069/apisecret/refreshsecret 602 | ``` 603 | 604 | ###### Post body 605 | 606 | ```json 607 | { 608 | "refresh_code":"3EbaSPUpKzHJ9wYgYZqy6W4g43NT365bm9vtTfYhMPra" 609 | } 610 | ``` 611 | 612 | The request includes the following parameters: 613 | 614 | ###### Post body 615 | 616 | | Field | Type | Note | Description | 617 | | :--- | :--- | :--- | :--- | 618 | | refresh_code | string | required | The corresponding refresh code of the API code specified in the X-API-CODE header | 619 | 620 | ##### Response Format 621 | 622 | An example of a successful response: 623 | 624 | ```json 625 | { 626 | "api_code": "4QjbY3qES4tEh19PU", 627 | "api_secret": "3jC1qjr4mrKxfoXkxoN27Uhmbm1E", 628 | "refresh_code": "HcN17gxZ3ojrBYSXnjKsU9Pun8krP6J9Pn678k4rZ13m" 629 | } 630 | ``` 631 | 632 | The response includes the following parameters: 633 | 634 | | Field | Type | Description | 635 | | :--- | :--- | :--- | 636 | | api_code | string | The new inactive API code | 637 | | api_secret | string | The API secret | 638 | | refresh_code | string | The paired refresh code | 639 | 640 | ##### Error Code 641 | 642 | | HTTP Code | Error Code | Error | Message | Description | 643 | | :--- | :--- | :--- | :--- | :--- | 644 | | 403 | - | Forbidden. Invalid ID | - | No merchant ID found | 645 | | 403 | - | Forbidden. Header not found | - | Missing `X-API-CODE`, `X-CHECKSUM` header or query param `t` | 646 | | 403 | - | Forbidden. Invalid timestamp | - | The timestamp `t` is not in the valid time range | 647 | | 403 | - | Forbidden. Invalid checksum | - | The request is considered a replayed request | 648 | | 403 | - | Forbidden. Invalid API code | - | `X-API-CODE` header contains invalid API code | 649 | | 403 | - | Forbidden. Checksum unmatch | - | `X-CHECKSUM` header contains wrong checksum | 650 | | 403 | - | Forbidden. Call too frequently ({THROTTLING_COUNT} calls/minute) | - | Send requests too frequently | 651 | | 400 | 112 | Invalid parameter | - | Malformatted post body or the refresh code is invalid | 652 | | 403 | 113 | Permission denied | - | Merchant API not allowed | 653 | 654 | ##### [Back to top](#table-of-contents) 655 | 656 | 657 | 658 | 659 | # Mock Server 660 | 661 | ### How to compile 662 | - Put sample code to {YOUR\_GO\_PATH}/github.com/cybavo/SOFA\_MOCK\_SERVER 663 | - Execute 664 | - glide install 665 | - go build ./mockserver.go 666 | - ./mockserver 667 | 668 | ### Setup configuration 669 | 670 | > Configure CYBAVO API server URL in mockserver.app.conf 671 | 672 | ``` 673 | api_server_url="BACKEND_SERVER_URL" 674 | ``` 675 | 676 | ### Put merchant API code/secret into mock server 677 | - Get API code/secret on web control panel 678 | - API_CODE, API\_SECRET, WALLET\_ID 679 | - Put API code/secret to mock server's database 680 | 681 | ``` 682 | curl -X POST -H "Content-Type: application/json" -d '{"api_code":"API_CODE","api_secret":"API_SECRET"}' \ 683 | http://localhost:8889/v1/mock/merchant/{MERCHANT_ID}/apitoken 684 | ``` 685 | 686 | ### Register mock server callback URL 687 | > Operate on web control panel 688 | 689 | Notification Callback URL 690 | 691 | ``` 692 | http://localhost:8889/v1/mock/merchant/callback 693 | ``` 694 | 695 | ##### [Back to top](#table-of-contents) 696 | 697 | 698 | # cURL Testing Commands 699 | 700 | 701 | ### Create Payment Order 702 | 703 | ``` 704 | curl -X POST -H "Content-Type: application/json" -d '{"currency":60,"token_address":"","amount":"0.01","duration":50,"description":"TEST Order","redirect_url":"","order_id":"N520335069_10001"}' \ 705 | http://localhost:8889/v1/mock/merchant/{MERCHANT_ID}/order 706 | ``` 707 | 708 | - [API definition](#create-payment-order) 709 | 710 | 711 | 712 | ### Query Payment Order Status 713 | 714 | ``` 715 | curl http://localhost:8889/v1/mock/merchant/{MERCHANT_ID}/order?token={ACCESS_TOKEN}&order={ORDER_ID} 716 | ``` 717 | 718 | - [API definition](#query-payment-order-status) 719 | 720 | 721 | 722 | ### Cancel Payment Order 723 | 724 | ``` 725 | curl -X DELETE http://localhost:8889/v1/mock/merchant/{MERCHANT_ID}/order?token={ACCESS_TOKEN}&order={ORDER_ID} 726 | ``` 727 | 728 | - [API definition](#cancel-payment-order) 729 | 730 | 731 | 732 | ### Update Payment Order Expiration Duration 733 | 734 | ``` 735 | curl -X POST -H "Content-Type: application/json" -d '{"access_token":"IUxyiWsdIBp_FS6Tu2afaecH4F_dqpYOLj4oXxn02AA","order_id":"N520335069_10001","duration":100}' \ 736 | http://localhost:8889/v1/mock/merchant/{MERCHANT_ID}/order/duration 737 | ``` 738 | 739 | - [API definition](#update-payment-order-expiration-duration) 740 | 741 | 742 | ### Resend Failed Merchant Callbacks 743 | 744 | ``` 745 | curl -X POST http://localhost:8889/v1/mock/merchant/{MERCHANT_ID}/notifications/manual 746 | ``` 747 | - [API definition](#resend-failed-merchant-callbacks) 748 | 749 | 750 | 751 | ### Activate Merchant API Code 752 | 753 | ``` 754 | curl -X POST http://localhost:8889/v1/mock/merchant/{MERCHANT_ID}/apisecret/activate 755 | ``` 756 | - [API definition](#activate-merchant-api-code) 757 | 758 | 759 | 760 | ### Query Merchant API Code Status 761 | 762 | ``` 763 | curl http://localhost:8889/v1/mock/merchant/{MERCHANT_ID}/apisecret 764 | ``` 765 | - [API definition](#query-merchant-api-code-status) 766 | 767 | 768 | 769 | ### Refresh Merchant API Code 770 | 771 | ``` 772 | curl -X POST -H "Content-Type: application/json" -d '{"refresh_code":"3EbaSPUpKzHJ9wYgYZqy6W4g43NT365bm9vtTfYhMPra"}' \ 773 | http://localhost:8889/v1/mock/merchant/{MERCHANT_ID}/apisecret/refreshsecret 774 | ``` 775 | - [API definition](#refresh-merchant-api-code) 776 | 777 | 778 | ##### [Back to top](#table-of-contents) 779 | 780 | 781 | # Other Language Versions 782 | - [Java](https://github.com/CYBAVO/SOFA_MOCK_SERVER_JAVA) 783 | - [Javascript](https://github.com/CYBAVO/SOFA_MOCK_SERVER_JAVASCRIPT) 784 | - [PHP](https://github.com/CYBAVO/SOFA_MOCK_SERVER_PHP) 785 | 786 | ##### [Back to top](#table-of-contents) 787 | 788 | # Appendix 789 | 790 | 791 | ### Callback Definition 792 | 793 |
| Field | 796 |Type | 797 |Description | 798 |||||||||||||
| merchant_id | 801 |int64 | 802 |The merchant ID of the callback | 803 |||||||||||||
| order_id | 806 |string | 807 |The unique order ID of the payment order | 808 |||||||||||||
| currency | 811 |string | 812 |Cryptocurrency of the callback | 813 |||||||||||||
| txid | 816 |string | 817 |Transaction identifier | 818 |||||||||||||
| block_height | 821 |int64 | 822 |The block height show the transaction was packed in which block | 823 |||||||||||||
| recv_amount | 826 |string | 827 |Received amount denominated in the smallest cryptocurrency unit | 828 |||||||||||||
| fee | 831 |string | 832 |Mining fee denominated in the smallest cryptocurrency unit | 833 |||||||||||||
| broadcast_at | 836 |int64 | 837 |When to broadcast the transaction in UTC time | 838 |||||||||||||
| from_address | 841 |string | 842 |The source address of the transaction | 843 |||||||||||||
| to_address | 846 |string | 847 |The destination address of the transaction | 848 |||||||||||||
| state | 851 |int | 852 |
853 | Possible states (listed in the Order State Definition table)
854 |
|
865 | ||||||||||||
| decimal | 868 |int | 869 |The decimal of cryptocurrency | 870 |||||||||||||
| fee_decimal | 873 |int | 874 |The decimal of cryptocurrency miner fee | 875 |||||||||||||
| addon | 878 |key-value pairs | 879 |880 | The extra information of this callback 881 | | 882 |||||||||||||
| currency_bip44 | 885 |int64 | 886 |887 | Refer to Currency Definition table 888 | | 889 |||||||||||||
| token_address | 892 |string | 893 |The contract address of cryptocurrency | 894 |
CYBAVO Merchant Service(CMS) is a comprehensive cryptocurrency payment servcie, based on the deposit wallet of CYBAVO VAULT.
370 | 371 |Please note that the merchant must have an activated API code, otherwise no callback will be sent.
434 | 435 |To ensure that the callbacks have processed by callback handler, the CYBAVO Merchant Service will continue to send the callbacks to the callback URL until a callback confirmation (HTTP/1.1 200 OK) is received or exceeds the number of retries (retry time interval: 1-3-5-15-45 mins).
439 | 440 |Refer to Callback Definition, Callback Type Definition for detailed definition.
Please refer to the code snippet on the github project to know how to validate the callback payload.
445 | 446 |Create a payment order.
461 | 462 |POST /v1/merchant/MERCHANT_ID/order
471 |475 | 476 |The order_id must be prefixed. Find prefix from corresponding merchant detail on web control panel.
472 | 473 |The prefix is
474 |N520335069_is the following example.
An example of the request:
479 | 480 |/v1/merchant/520335069/order{
487 | "currency": 60,
488 | "token_address": "",
489 | "amount": "0.01",
490 | "duration": 50,
491 | "description": "ETH payment",
492 | "order_id": "N520335069_1000022",
493 | "redirect_url": "https%3A%2F%2Fmyredirect.example.com%3Fk%3Dv%26k1%3Dv1"
494 | }The request includes the following parameters:
497 | 498 || Field | 504 |Type | 505 |Note | 506 |Description | 507 |
|---|---|---|---|
| currency | 513 |int64 | 514 |required, refer to the Currency Definition | 515 |The cryptocurrency used to pay the order | 516 |
| token_address | 519 |string | 520 |optional | 521 |The token contract address of cryptocurrency used to pay the order | 522 |
| amount | 525 |string | 526 |required, refer to the definition for the default maximum amount | 527 |The required amount of the payment order | 528 |
| duration | 531 |int64 | 532 |optional, refer to the definition for the minimum duration | 533 |The expiration duration (in minutes) of the payment order | 534 |
| description | 537 |string | 538 |optional, max 255 chars |
539 | The description of the payment order | 540 |
| order_id | 543 |string | 544 |required | 545 |The user defined order ID (must be prefixed) | 546 |
| redirect_url | 549 |string | 550 |optional | 551 |User defined redirect URL (must be encoded) | 552 |
557 |561 | 562 | 564 | 565 |The
558 | 559 |redirect_urlmust be encoded. Please refer to the code snippet on the github project to know how to encode the URL. Go, Java, Javascript, PHPIf using BNB or XLM payment order, the payment transaction must specify an accurate memo to complete the order.
560 |
| ID | 569 |Currency Symbol | 570 |Minimum Duration | 571 |Default Duration | 572 |Default Minimum / Maximum Amount | 573 |Confirm Blocks | 574 |
|---|---|---|---|---|---|
| 0 | 580 |BTC | 581 |20 | 582 |120 | 583 |0.00002 / 0.2 | 584 |2 | 585 |
| 2 | 588 |LTC | 589 |20 | 590 |120 | 591 |0.005 / 50 | 592 |6 | 593 |
| 3 | 596 |DOGE | 597 |20 | 598 |120 | 599 |0.005 / 50 | 600 |15 | 601 |
| 60 | 604 |ETH | 605 |10 | 606 |60 | 607 |0.0005 / 5 | 608 |6 | 609 |
| 145 | 612 |BCH | 613 |20 | 614 |120 | 615 |0.005 / 20 | 616 |6 | 617 |
| 148 | 620 |XLM | 621 |10 | 622 |30 | 623 |2 / 2000 | 624 |1 | 625 |
| 195 | 628 |TRX | 629 |10 | 630 |30 | 631 |10 / 10000 | 632 |1 | 633 |
| 714 | 636 |BNB | 637 |10 | 638 |30 | 639 |0.003 / 30 | 640 |1 | 641 |
| 966 | 644 |MATIC | 645 |10 | 646 |60 | 647 |0.005 / 50 | 648 |50 | 649 |
| 1815 | 652 |ADA | 653 |10 | 654 |30 | 655 |1 / 1000 | 656 |30 | 657 |
| 99999999997 | 660 |BSC | 661 |10 | 662 |60 | 663 |0.005 / 50 | 664 |20 | 665 |
670 |672 | 673 |The
671 |Maximum Amountboundary can be adjusted in the web control panel.
An example of a successful response:
676 | 677 |{
678 | "access_token": "ybJWKM_CT1yXxzLO2z1Y5fg1EzHuMyRA14ubzR8i-RE",
679 | "address": "0xed965D0A23eC4583f55Fb5d4109C0fE069B396fC",
680 | "expired_time": 1615975467,
681 | "order_id": "N520335069_1000022"
682 | }For BNB, XLM
685 | 686 |{
687 | "access_token": "ybJWKM_CT1yXxzLO2z1Y5fg1EzHuMyRA14ubzR8i-RE",
688 | "address": "0xed965D0A23eC4583f55Fb5d4109C0fE069B396fC",
689 | "expired_time": 1615975467,
690 | "order_id": "N520335069_1000023",
691 | "memo": "63574"
692 | }The response includes the following parameters:
695 | 696 || Field | 700 |Type | 701 |Description | 702 |
|---|---|---|
| access_token | 708 |string | 709 |The access token used to query, update or cancel the payment order | 710 |
| address | 713 |string | 714 |The address to accept the payment | 715 |
| expired_time | 718 |int64 | 719 |The due date of the payment order (unix time in UTC) | 720 |
| order_id | 723 |string | 724 |The order ID of the payment order | 725 |
| memo | 728 |string | 729 |The memo of the payment order, the payment transaction must specify an accurate memo to complete the order. | 730 |
| HTTP Code | 740 |Error Code | 741 |Error | 742 |Message | 743 |Description | 744 |
|---|---|---|---|---|
| 403 | 750 |- | 751 |Forbidden. Invalid ID | 752 |- | 753 |No merchant ID found | 754 |
| 403 | 757 |- | 758 |Forbidden. Header not found | 759 |- | 760 |Missing X-API-CODE, X-CHECKSUM header or query param t |
761 |
| 403 | 764 |- | 765 |Forbidden. Invalid timestamp | 766 |- | 767 |The timestamp t is not in the valid time range |
768 |
| 403 | 771 |- | 772 |Forbidden. Invalid checksum | 773 |- | 774 |The request is considered a replayed request | 775 |
| 403 | 778 |- | 779 |Forbidden. Invalid API code | 780 |- | 781 |X-API-CODE header contains invalid API code |
782 |
| 403 | 785 |- | 786 |Forbidden. Checksum unmatch | 787 |- | 788 |X-CHECKSUM header contains wrong checksum |
789 |
| 403 | 792 |- | 793 |Forbidden. Call too frequently ({THROTTLING_COUNT} calls/minute) | 794 |- | 795 |Send requests too frequently | 796 |
| 400 | 799 |112 | 800 |Invalid parameter | 801 |- | 802 |Malformatted post body | 803 |
| 403 | 806 |113 | 807 |Permission denied | 808 |- | 809 |Merchant API not allowed | 810 |
| 400 | 813 |20003 | 814 |Wallet address not available | 815 |- | 816 |There is no available deposit address | 817 |
| 400 | 820 |20007 | 821 |Wallet not found | 822 |- | 823 |No corresponding linked deposit wallet found (specified by the currency and token address in the request) | 824 |
| 400 | 827 |20009 | 828 |Duplicated Order ID | 829 |- | 830 |The order ID has been used | 831 |
| 400 | 834 |20010 | 835 |Wrong Order prefix | 836 |- | 837 |The order prefix is wrong | 838 |
| 400 | 841 |20011 | 842 |Wrong Order format | 843 |- | 844 |The order contains invalid characters | 845 |
| 400 | 848 |20014 | 849 |Wrong order amount | 850 |- | 851 |The decimals of the amount does not conform to currency's decimals | 852 |
| 400 | 855 |20015 | 856 |Invalid order duration, should not be less than minimum | 857 |- | 858 |The requested duration is less than the lower bound | 859 |
| 400 | 862 |20016 | 863 |Over amount limit | 864 |- | 865 |The requested amount exceeds upper bound | 866 |
| 400 | 869 |20017 | 870 |Invalid url host | 871 |- | 872 |The redirect URL is invalid | 873 |
Query current status of a payment order.
884 | 885 |VIEW GET /v1/merchant/MERCHANT_ID/order?token=ACCESS_TOKEN&order=ORDER_ID
An example of the request:
896 | 897 |/v1/merchant/520335069/order?token=ybJWKM_CT1yXxzLO2z1Y5fg1EzHuMyRA14ubzR8i-RE&order=N520335069_1000022An example of a successful response:
904 | 905 |{
906 | "address": "0xed965D0A23eC4583f55Fb5d4109C0fE069B396fC",
907 | "expired_time": 1615975468,
908 | "redirect_url": "https%3A%2F%2Fmyredirect.example.com%3Fk%3Dv%26k1%3Dv1",
909 | "state": 1,
910 | "tx_id": ""
911 | }The response includes the following parameters:
914 | 915 || Field | 919 |Type | 920 |Description | 921 |
|---|---|---|
| address | 927 |string | 928 |The deposit address to accept payment | 929 |
| expired_time | 932 |int64 | 933 |The due date of the payment (unix time in UTC) | 934 |
| redirect_url | 937 |string | 938 |User defined redirect URL (encoded) | 939 |
| state | 942 |int | 943 |Refer to Order State Definition | 944 |
| tx_id | 947 |string | 948 |The TX ID of the payment if state is 0, 2 or 3 | 949 |
954 |956 | 957 |The
955 |redirect_urlis encoded. Please refer to the code snippet on the github project to know how to decode the URL. Go, Java, Javascript, PHP
| HTTP Code | 963 |Error Code | 964 |Error | 965 |Message | 966 |Description | 967 |
|---|---|---|---|---|
| 403 | 973 |- | 974 |Forbidden. Invalid ID | 975 |- | 976 |No merchant ID found | 977 |
| 403 | 980 |- | 981 |Forbidden. Header not found | 982 |- | 983 |Missing X-API-CODE, X-CHECKSUM header or query param t |
984 |
| 403 | 987 |- | 988 |Forbidden. Invalid timestamp | 989 |- | 990 |The timestamp t is not in the valid time range |
991 |
| 403 | 994 |- | 995 |Forbidden. Invalid checksum | 996 |- | 997 |The request is considered a replayed request | 998 |
| 403 | 1001 |- | 1002 |Forbidden. Invalid API code | 1003 |- | 1004 |X-API-CODE header contains invalid API code |
1005 |
| 403 | 1008 |- | 1009 |Forbidden. Checksum unmatch | 1010 |- | 1011 |X-CHECKSUM header contains wrong checksum |
1012 |
| 403 | 1015 |- | 1016 |Forbidden. Call too frequently ({THROTTLING_COUNT} calls/minute) | 1017 |- | 1018 |Send requests too frequently | 1019 |
| 403 | 1022 |112 | 1023 |Invalid parameter | 1024 |- | 1025 |The invalid ACCESS_TOKEN/ORDER_ID | 1026 |
| 403 | 1029 |113 | 1030 |Permission denied | 1031 |- | 1032 |Merchant API not allowed | 1033 |
Cancel a payment order. Only pending payment order can be cancelled.
1044 | 1045 |DELETE /v1/merchant/MERCHANT_ID/order?token=ACCESS_TOKEN&order=ORDER_ID
An example of the request:
1056 | 1057 |/v1/merchant/520335069/order?token=ybJWKM_CT1yXxzLO2z1Y5fg1EzHuMyRA14ubzR8i-RE&order=N520335069_1000022An example of a successful response:
1064 | 1065 |{
1066 | "result": 1
1067 | }The response includes the following parameters:
1070 | 1071 || Field | 1075 |Type | 1076 |Description | 1077 |
|---|---|---|
| result | 1083 |int | 1084 |Always be 1, means the payment order has been successfully cancelled | 1085 |
| HTTP Code | 1095 |Error Code | 1096 |Error | 1097 |Message | 1098 |Description | 1099 |
|---|---|---|---|---|
| 403 | 1105 |- | 1106 |Forbidden. Invalid ID | 1107 |- | 1108 |No merchant ID found | 1109 |
| 403 | 1112 |- | 1113 |Forbidden. Header not found | 1114 |- | 1115 |Missing X-API-CODE, X-CHECKSUM header or query param t |
1116 |
| 403 | 1119 |- | 1120 |Forbidden. Invalid timestamp | 1121 |- | 1122 |The timestamp t is not in the valid time range |
1123 |
| 403 | 1126 |- | 1127 |Forbidden. Invalid checksum | 1128 |- | 1129 |The request is considered a replayed request | 1130 |
| 403 | 1133 |- | 1134 |Forbidden. Invalid API code | 1135 |- | 1136 |X-API-CODE header contains invalid API code |
1137 |
| 403 | 1140 |- | 1141 |Forbidden. Checksum unmatch | 1142 |- | 1143 |X-CHECKSUM header contains wrong checksum |
1144 |
| 403 | 1147 |- | 1148 |Forbidden. Call too frequently ({THROTTLING_COUNT} calls/minute) | 1149 |- | 1150 |Send requests too frequently | 1151 |
| 403 | 1154 |112 | 1155 |Invalid parameter | 1156 |- | 1157 |The invalid ACCESS_TOKEN/ORDER_ID or the payment can't be cancelled | 1158 |
| 403 | 1161 |113 | 1162 |Permission denied | 1163 |- | 1164 |Merchant API not allowed | 1165 |
Update a payment order expiration duration. Only pending payment order can be updated.
1176 | 1177 |POST /v1/merchant/MERCHANT_ID/order/duration
An example of the request:
1186 | 1187 |/v1/merchant/520335069/order{
1194 | "access_token": "ybJWKM_CT1yXxzLO2z1Y5fg1EzHuMyRA14ubzR8i-RE",
1195 | "order_id": "N520335069_1000022",
1196 | "duration": 50
1197 | }The request includes the following parameters:
1200 | 1201 || Field | 1207 |Type | 1208 |Note | 1209 |Description | 1210 |
|---|---|---|---|
| access_token | 1216 |string | 1217 |required | 1218 |The access token of the payment order returned with Create order API | 1219 |
| order_id | 1222 |string | 1223 |required | 1224 |The order ID of the payment order | 1225 |
| duration | 1228 |int64 | 1229 |required | 1230 |The expiration duration (in minutes) of the payment order | 1231 |
1236 |1238 | 1239 |The new due date is calculated based on the submission time of the payment order.
1237 |
An example of a successful response:
1242 | 1243 |{
1244 | "result": 1
1245 | }The response includes the following parameters:
1248 | 1249 || Field | 1253 |Type | 1254 |Description | 1255 |
|---|---|---|
| result | 1261 |int | 1262 |Always be 1, means the payment order has been successfully updated | 1263 |
| HTTP Code | 1273 |Error Code | 1274 |Error | 1275 |Message | 1276 |Description | 1277 |
|---|---|---|---|---|
| 403 | 1283 |- | 1284 |Forbidden. Invalid ID | 1285 |- | 1286 |No merchant ID found | 1287 |
| 403 | 1290 |- | 1291 |Forbidden. Header not found | 1292 |- | 1293 |Missing X-API-CODE, X-CHECKSUM header or query param t |
1294 |
| 403 | 1297 |- | 1298 |Forbidden. Invalid timestamp | 1299 |- | 1300 |The timestamp t is not in the valid time range |
1301 |
| 403 | 1304 |- | 1305 |Forbidden. Invalid checksum | 1306 |- | 1307 |The request is considered a replayed request | 1308 |
| 403 | 1311 |- | 1312 |Forbidden. Invalid API code | 1313 |- | 1314 |X-API-CODE header contains invalid API code |
1315 |
| 403 | 1318 |- | 1319 |Forbidden. Checksum unmatch | 1320 |- | 1321 |X-CHECKSUM header contains wrong checksum |
1322 |
| 403 | 1325 |- | 1326 |Forbidden. Call too frequently ({THROTTLING_COUNT} calls/minute) | 1327 |- | 1328 |Send requests too frequently | 1329 |
| 403 | 1332 |112 | 1333 |Invalid parameter | 1334 |- | 1335 |The invalid ACCESS_TOKEN/ORDER_ID or the payment can't be updated | 1336 |
| 403 | 1339 |113 | 1340 |Permission denied | 1341 |- | 1342 |Merchant API not allowed | 1343 |
The callback handler can call this API to resend pending or failed merchant callbacks.
1354 | 1355 |Refer to Callback Integration for callback rules.
1356 | 1357 |1358 |1360 | 1361 |The resend operation could be requested on the web control panel as well.
1359 |
POST /v1/merchant/MERCHANT_ID/notifications/manual
An example of the request:
1372 | 1373 |/v1/merchant/520335069/notifications/manualAn example of a successful response:
1380 | 1381 |{
1382 | "count": 0
1383 | }The response includes the following parameters:
1386 | 1387 || Field | 1391 |Type | 1392 |Description | 1393 |
|---|---|---|
| count | 1399 |int | 1400 |Count of callbacks just resent | 1401 |
| HTTP Code | 1411 |Error Code | 1412 |Error | 1413 |Message | 1414 |Description | 1415 |
|---|---|---|---|---|
| 403 | 1421 |- | 1422 |Forbidden. Invalid ID | 1423 |- | 1424 |No merchant ID found | 1425 |
| 403 | 1428 |- | 1429 |Forbidden. Header not found | 1430 |- | 1431 |Missing X-API-CODE, X-CHECKSUM header or query param t |
1432 |
| 403 | 1435 |- | 1436 |Forbidden. Invalid timestamp | 1437 |- | 1438 |The timestamp t is not in the valid time range |
1439 |
| 403 | 1442 |- | 1443 |Forbidden. Invalid checksum | 1444 |- | 1445 |The request is considered a replayed request | 1446 |
| 403 | 1449 |- | 1450 |Forbidden. Invalid API code | 1451 |- | 1452 |X-API-CODE header contains invalid API code |
1453 |
| 403 | 1456 |- | 1457 |Forbidden. Checksum unmatch | 1458 |- | 1459 |X-CHECKSUM header contains wrong checksum |
1460 |
| 403 | 1463 |- | 1464 |Forbidden. Call too frequently ({THROTTLING_COUNT} calls/minute) | 1465 |- | 1466 |Send requests too frequently | 1467 |
| 403 | 1470 |113 | 1471 |Permission denied | 1472 |- | 1473 |Merchant API not allowed | 1474 |
Activate the API code of a certain merchant. Once activated, the currently activated API code will immediately become invalid.
1485 | 1486 |POST /v1/merchant/MERCHANT_ID/apisecret/activate
An example of the request:
1497 | 1498 |/v1/merchant/520335069/apisecret/activateAn example of a successful response:
1505 | 1506 |{
1507 | "api_code": "4PcdE9VjXfrk7WjC1",
1508 | "exp": 1609646716
1509 | }The response includes the following parameters:
1512 | 1513 || Field | 1517 |Type | 1518 |Description | 1519 |
|---|---|---|
| api_code | 1525 |string | 1526 |The activated API code | 1527 |
| exp | 1530 |int64 | 1531 |The API code expiration unix time in UTC | 1532 |
| HTTP Code | 1542 |Error Code | 1543 |Error | 1544 |Message | 1545 |Description | 1546 |
|---|---|---|---|---|
| 403 | 1552 |- | 1553 |Forbidden. Invalid ID | 1554 |- | 1555 |No merchant ID found | 1556 |
| 403 | 1559 |- | 1560 |Forbidden. Header not found | 1561 |- | 1562 |Missing X-API-CODE, X-CHECKSUM header or query param t |
1563 |
| 403 | 1566 |- | 1567 |Forbidden. Invalid timestamp | 1568 |- | 1569 |The timestamp t is not in the valid time range |
1570 |
| 403 | 1573 |- | 1574 |Forbidden. Invalid checksum | 1575 |- | 1576 |The request is considered a replayed request | 1577 |
| 403 | 1580 |- | 1581 |Forbidden. Invalid API code | 1582 |- | 1583 |X-API-CODE header contains invalid API code |
1584 |
| 403 | 1587 |- | 1588 |Forbidden. Checksum unmatch | 1589 |- | 1590 |X-CHECKSUM header contains wrong checksum |
1591 |
| 403 | 1594 |- | 1595 |Forbidden. Call too frequently ({THROTTLING_COUNT} calls/minute) | 1596 |- | 1597 |Send requests too frequently | 1598 |
| 403 | 1601 |113 | 1602 |Permission denied | 1603 |- | 1604 |Merchant API not allowed | 1605 |
Query the API code info of a certain merchant. Use the inactivated API code in any request will activate it. Once activated, the currently activated API code will immediately become invalid.
VIEW GET /v1/merchant/MERCHANT_ID/apisecret
An example of the request:
1628 | 1629 |/v1/merchant/520335069/apisecretAn example of a successful response:
1636 | 1637 |{
1638 | "valid": {
1639 | "api_code": "H4Q6xFZgiTZb37GN",
1640 | "exp": 1583144863
1641 | },
1642 | "inactivated": {
1643 | "api_code": "32PmGCjNzXda4mNHX"
1644 | }
1645 | }The response includes the following parameters:
1648 | 1649 || Field | 1653 |Type | 1654 |Description | 1655 |
|---|---|---|
| valid | 1661 |object | 1662 |The activated API code | 1663 |
| inactivated | 1666 |object | 1667 |Not active API code | 1668 |
| api_code | 1671 |string | 1672 |The API code for querying wallet | 1673 |
| exp | 1676 |int64 | 1677 |The API code expiration unix time in UTC | 1678 |
1683 |1685 | 1686 |Use an invalid API-CODE, the caller will get a 403 Forbidden error.
1684 |
| HTTP Code | 1692 |Error Code | 1693 |Error | 1694 |Message | 1695 |Description | 1696 |
|---|---|---|---|---|
| 403 | 1702 |- | 1703 |Forbidden. Invalid ID | 1704 |- | 1705 |No merchant ID found | 1706 |
| 403 | 1709 |- | 1710 |Forbidden. Header not found | 1711 |- | 1712 |Missing X-API-CODE, X-CHECKSUM header or query param t |
1713 |
| 403 | 1716 |- | 1717 |Forbidden. Invalid timestamp | 1718 |- | 1719 |The timestamp t is not in the valid time range |
1720 |
| 403 | 1723 |- | 1724 |Forbidden. Invalid checksum | 1725 |- | 1726 |The request is considered a replayed request | 1727 |
| 403 | 1730 |- | 1731 |Forbidden. Invalid API code | 1732 |- | 1733 |X-API-CODE header contains invalid API code |
1734 |
| 403 | 1737 |- | 1738 |Forbidden. Checksum unmatch | 1739 |- | 1740 |X-CHECKSUM header contains wrong checksum |
1741 |
| 403 | 1744 |- | 1745 |Forbidden. Call too frequently ({THROTTLING_COUNT} calls/minute) | 1746 |- | 1747 |Send requests too frequently | 1748 |
| 403 | 1751 |113 | 1752 |Permission denied | 1753 |- | 1754 |Merchant API not allowed | 1755 |
Use paired refresh code to acquire the new inactive API code/secret of the merchant.
1766 | 1767 |POST /v1/merchant/MERCHANT_ID/apisecret/refreshsecret
An example of the request:
1778 | 1779 |/v1/merchant/520335069/apisecret/refreshsecret{
1786 | "refresh_code":"3EbaSPUpKzHJ9wYgYZqy6W4g43NT365bm9vtTfYhMPra"
1787 | }The request includes the following parameters:
1790 | 1791 || Field | 1797 |Type | 1798 |Note | 1799 |Description | 1800 |
|---|---|---|---|
| refresh_code | 1806 |string | 1807 |required | 1808 |The corresponding refresh code of the API code specified in the X-API-CODE header | 1809 |
An example of a successful response:
1816 | 1817 |{
1818 | "api_code": "4QjbY3qES4tEh19PU",
1819 | "api_secret": "3jC1qjr4mrKxfoXkxoN27Uhmbm1E",
1820 | "refresh_code": "HcN17gxZ3ojrBYSXnjKsU9Pun8krP6J9Pn678k4rZ13m"
1821 | }The response includes the following parameters:
1824 | 1825 || Field | 1829 |Type | 1830 |Description | 1831 |
|---|---|---|
| api_code | 1837 |string | 1838 |The new inactive API code | 1839 |
| api_secret | 1842 |string | 1843 |The API secret | 1844 |
| refresh_code | 1847 |string | 1848 |The paired refresh code | 1849 |
| HTTP Code | 1859 |Error Code | 1860 |Error | 1861 |Message | 1862 |Description | 1863 |
|---|---|---|---|---|
| 403 | 1869 |- | 1870 |Forbidden. Invalid ID | 1871 |- | 1872 |No merchant ID found | 1873 |
| 403 | 1876 |- | 1877 |Forbidden. Header not found | 1878 |- | 1879 |Missing X-API-CODE, X-CHECKSUM header or query param t |
1880 |
| 403 | 1883 |- | 1884 |Forbidden. Invalid timestamp | 1885 |- | 1886 |The timestamp t is not in the valid time range |
1887 |
| 403 | 1890 |- | 1891 |Forbidden. Invalid checksum | 1892 |- | 1893 |The request is considered a replayed request | 1894 |
| 403 | 1897 |- | 1898 |Forbidden. Invalid API code | 1899 |- | 1900 |X-API-CODE header contains invalid API code |
1901 |
| 403 | 1904 |- | 1905 |Forbidden. Checksum unmatch | 1906 |- | 1907 |X-CHECKSUM header contains wrong checksum |
1908 |
| 403 | 1911 |- | 1912 |Forbidden. Call too frequently ({THROTTLING_COUNT} calls/minute) | 1913 |- | 1914 |Send requests too frequently | 1915 |
| 400 | 1918 |112 | 1919 |Invalid parameter | 1920 |- | 1921 |Malformatted post body or the refresh code is invalid | 1922 |
| 403 | 1925 |113 | 1926 |Permission denied | 1927 |- | 1928 |Merchant API not allowed | 1929 |
1955 |1957 | 1958 |Configure CYBAVO API server URL in mockserver.app.conf
1956 |
api_server_url="BACKEND_SERVER_URL"curl -X POST -H "Content-Type: application/json" -d '{"api_code":"API_CODE","api_secret":"API_SECRET"}' \
1972 | http://localhost:8889/v1/mock/merchant/{MERCHANT_ID}/apitoken1977 |1979 | 1980 |Operate on web control panel
1978 |
Notification Callback URL
1981 | 1982 |http://localhost:8889/v1/mock/merchant/callbackcurl -X POST -H "Content-Type: application/json" -d '{"currency":60,"token_address":"","amount":"0.01","duration":50,"description":"TEST Order","redirect_url":"","order_id":"N520335069_10001"}' \
1995 | http://localhost:8889/v1/mock/merchant/{MERCHANT_ID}/ordercurl http://localhost:8889/v1/mock/merchant/{MERCHANT_ID}/order?token={ACCESS_TOKEN}&order={ORDER_ID}curl -X DELETE http://localhost:8889/v1/mock/merchant/{MERCHANT_ID}/order?token={ACCESS_TOKEN}&order={ORDER_ID}curl -X POST -H "Content-Type: application/json" -d '{"access_token":"IUxyiWsdIBp_FS6Tu2afaecH4F_dqpYOLj4oXxn02AA","order_id":"N520335069_10001","duration":100}' \
2026 | http://localhost:8889/v1/mock/merchant/{MERCHANT_ID}/order/durationcurl -X POST http://localhost:8889/v1/mock/merchant/{MERCHANT_ID}/notifications/manualcurl -X POST http://localhost:8889/v1/mock/merchant/{MERCHANT_ID}/apisecret/activatecurl http://localhost:8889/v1/mock/merchant/{MERCHANT_ID}/apisecretcurl -X POST -H "Content-Type: application/json" -d '{"refresh_code":"3EbaSPUpKzHJ9wYgYZqy6W4g43NT365bm9vtTfYhMPra"}' \
2067 | http://localhost:8889/v1/mock/merchant/{MERCHANT_ID}/apisecret/refreshsecret| Field | 2096 |Type | 2097 |Description | 2098 |||||||||||||
| merchant_id | 2101 |int64 | 2102 |The merchant ID of the callback | 2103 |||||||||||||
| order_id | 2106 |string | 2107 |The unique order ID of the payment order | 2108 |||||||||||||
| currency | 2111 |string | 2112 |Cryptocurrency of the callback | 2113 |||||||||||||
| txid | 2116 |string | 2117 |Transaction identifier | 2118 |||||||||||||
| block_height | 2121 |int64 | 2122 |The block height show the transaction was packed in which block | 2123 |||||||||||||
| recv_amount | 2126 |string | 2127 |Received amount denominated in the smallest cryptocurrency unit | 2128 |||||||||||||
| fee | 2131 |string | 2132 |Mining fee denominated in the smallest cryptocurrency unit | 2133 |||||||||||||
| broadcast_at | 2136 |int64 | 2137 |When to broadcast the transaction in UTC time | 2138 |||||||||||||
| from_address | 2141 |string | 2142 |The source address of the transaction | 2143 |||||||||||||
| to_address | 2146 |string | 2147 |The destination address of the transaction | 2148 |||||||||||||
| state | 2151 |int | 2152 |
2153 | Possible states (listed in the Order State Definition table)
2154 |
|
2165 | ||||||||||||
| decimal | 2168 |int | 2169 |The decimal of cryptocurrency | 2170 |||||||||||||
| fee_decimal | 2173 |int | 2174 |The decimal of cryptocurrency miner fee | 2175 |||||||||||||
| addon | 2178 |key-value pairs | 2179 |2180 | The extra information of this callback 2181 | | 2182 |||||||||||||
| currency_bip44 | 2185 |int64 | 2186 |2187 | Refer to Currency Definition table 2188 | | 2189 |||||||||||||
| token_address | 2192 |string | 2193 |The contract address of cryptocurrency | 2194 |
| ID | 2207 |Description | 2208 |
|---|---|
| -1 | 2214 |Waiting for payment | 2215 |
| 0 | 2218 |The order has been successfully paid | 2219 |
| 1 | 2222 |The order has expired | 2223 |
| 2 | 2226 |The amount received is less than the amount requested | 2227 |
| 3 | 2230 |The amount received is greater than the amount requested | 2231 |
| 4 | 2234 |The order cancelled | 2235 |