// lock sets set the 'open' property of an object to false. Also resets all fields used by // all open methods func (c *ObjectController) Lock(params martini.Params, res http.ResponseWriter, req services.AuxRequestContext, log *config.CustomLog, db *services.DB) { // TODO: get from access token // authorizing wallet id authWalletID := "55c679145fe09c74ed000001" dbTx, err := db.GetPostgresHandleWithRepeatableReadTrans() if err != nil { c.log.Error(err.Error()) services.Res(res).Error(500, "", "server error") return } // get the object object, found, err := models.FindObjectByObjectID(dbTx, params["id"]) if !found { dbTx.Rollback() services.Res(res).Error(404, "not_found", "object was not found") return } else if err != nil { dbTx.Rollback() c.log.Error(err.Error()) services.Res(res).Error(500, "", "server error") return } // ensure object belongs to authorizing wallet if object.Wallet.ObjectID != authWalletID { dbTx.Rollback() services.Res(res).Error(401, "unauthorized", "objects: object does not belong to authorizing wallet") return } // clear open related fields of object clearOpen(&object) // save update and commit dbTx.Save(&object).Commit() services.Res(res).Json(object) }
// create a new object by subtracting from a source object func (c *ObjectController) Subtract(res http.ResponseWriter, req services.AuxRequestContext, log *config.CustomLog, db *services.DB) { // TODO: get from access token // authorizing wallet id authWalletID := "55c679145fe09c74ed000001" // parse body var body objectSubtractBody if err := c.ParseJsonBody(req, &body); err != nil { services.Res(res).Error(400, "invalid_body", "request body is invalid or malformed. Expects valid json body") return } // object is required if c.validate.IsEmpty(body.Object) { services.Res(res).Error(400, "missing_parameter", "Missing required field: object") return } // amount to subtract is required if body.AmountToSubtract < MinimumObjectUnit { services.Res(res).Error(400, "invalid_parameter", "amount: amount must be equal or greater than the minimum object unit which is 0.00000001") return } dbTx, err := db.GetPostgresHandleWithRepeatableReadTrans() if err != nil { c.log.Error(err.Error()) services.Res(res).Error(500, "", "server error") return } // get the object object, found, err := models.FindObjectByObjectID(dbTx, body.Object) if !found { dbTx.Rollback() services.Res(res).Error(404, "not_found", "object was not found") return } else if err != nil { dbTx.Rollback() c.log.Error(err.Error()) services.Res(res).Error(500, "", "server error") return } // ensure object is a valuable type if object.Type == models.ObjectValueless { dbTx.Rollback() services.Res(res).Error(400, "invalid_parameter", "object: object must be a valuabe type (obj_value) ") return } // ensure object belongs to authorizing wallet if object.Wallet.ObjectID != authWalletID { dbTx.Rollback() services.Res(res).Error(401, "unauthorized", "objects: object does not belong to authorizing wallet") return } // ensure object's balance is sufficient if object.Balance < body.AmountToSubtract { dbTx.Rollback() services.Res(res).Error(400, "invalid_parameter", "amount: object's balance is insufficient") return } // if meta is provided, ensure it is not greater than the limit size if !body.InheritMeta && !c.validate.IsEmpty(body.Meta) && len([]byte(body.Meta)) > MaxMetaSize { services.Res(res).Error(400, "invalid_meta_size", fmt.Sprintf("Meta contains too much data. Max size is %d bytes", MaxMetaSize)) return } else { if body.InheritMeta { body.Meta = object.Meta } } // subtract and update object's balance object.Balance = object.Balance - body.AmountToSubtract dbTx.Save(&object) // create new object // generate a pin countryCallCode := config.CurrencyCallCodes[strings.ToUpper(object.Service.Identity.BaseCurrency)] newPin, err := services.NewObjectPin(strconv.Itoa(countryCallCode)) if err != nil { dbTx.Rollback() c.log.Error(err.Error()) services.Res(res).Error(500, "", "server error") return } newObj := NewObject(newPin, models.ObjectValue, object.Service, object.Wallet, body.AmountToSubtract, body.Meta) err = models.CreateObject(dbTx, &newObj) if err != nil { dbTx.Rollback() c.log.Error(err.Error()) services.Res(res).Error(500, "", "server error") return } dbTx.Commit() services.Res(res).Json(newObj) }
// open an object for charge/consumption. An object opened in this method // will be consumable without restriction func (c *ObjectController) Open(params martini.Params, res http.ResponseWriter, req services.AuxRequestContext, log *config.CustomLog, db *services.DB) { // TODO: get from access token // authorizing wallet id authWalletID := "55c679145fe09c74ed000001" // parse body var body objectOpenBody if err := c.ParseJsonBody(req, &body); err != nil { services.Res(res).Error(400, "invalid_body", "request body is invalid or malformed. Expects valid json body") return } // ensure open method is provided if c.validate.IsEmpty(body.OpenMethod) { services.Res(res).Error(400, "invalid_parameter", "open_method: open method is required") return } // ensure a known open method is provided if body.OpenMethod != "open" && body.OpenMethod != "open_timed" && body.OpenMethod != "open_pin" { services.Res(res).Error(400, "invalid_parameter", "unknown open type method") return } dbTx, err := db.GetPostgresHandleWithRepeatableReadTrans() if err != nil { c.log.Error(err.Error()) services.Res(res).Error(500, "", "server error") return } // get the object object, found, err := models.FindObjectByObjectID(dbTx, params["id"]) if !found { dbTx.Rollback() services.Res(res).Error(404, "not_found", "object was not found") return } else if err != nil { dbTx.Rollback() c.log.Error(err.Error()) services.Res(res).Error(500, "", "server error") return } // ensure object belongs to authorizing wallet if object.Wallet.ObjectID != authWalletID { dbTx.Rollback() services.Res(res).Error(401, "unauthorized", "objects: object does not belong to authorizing wallet") return } // set object's open property to true and open_method to `open` clearOpen(&object) object.Open = true object.OpenMethod = models.ObjectOpenDefault // for open_timed, // set 'open_time' field to indicate object open window if body.OpenMethod == "open_timed" { // ensure time field is provided if body.Time == 0 { dbTx.Rollback() services.Res(res).Error(400, "invalid_parameter", "time: open window time is required. use unix time") return } // time must be in the future now := time.Now().UTC() if !now.Before(services.UnixToTime(body.Time).UTC()) { dbTx.Rollback() services.Res(res).Error(400, "invalid_parameter", "time: use a unix time pointing to a period in the future") return } object.OpenMethod = models.ObjectOpenTimed object.OpenTime = body.Time } // for open_pin // open pin sets a pin for used by charge API if body.OpenMethod == "open_pin" { // ensure pin is provided if c.validate.IsEmpty(body.Pin) { dbTx.Rollback() services.Res(res).Error(400, "invalid_parameter", "pin: pin is required") return } // pin must be numeric if !validator.IsNumeric(body.Pin) { dbTx.Rollback() services.Res(res).Error(400, "invalid_parameter", "pin: pin must contain only numeric characters. e.g 4345") return } // pin length must be between 4 - 12 characters if len(body.Pin) < 4 || len(body.Pin) > 12 { dbTx.Rollback() services.Res(res).Error(400, "invalid_parameter", "pin: pin must have a minimum character length of 4 and maximum of 12") return } // hash pin using bcrypt pinHash, err := services.Bcrypt(body.Pin, 10) if err != nil { c.log.Error("unable to hash password. reason: " + err.Error()) services.Res(res).Error(500, "", "server error") return } object.OpenMethod = models.ObjectOpenPin object.OpenPin = pinHash } dbTx.Save(&object).Commit() services.Res(res).Json(object) }
// divide an object into two or more equal parts. // maxinum of 100 equal parts is allowed. // object to be splitted must belong to authorizing wallet func (c *ObjectController) Divide(res http.ResponseWriter, req services.AuxRequestContext, log *config.CustomLog, db *services.DB) { // authorizing wallet id // todo: get from access token authWalletID := "55c679145fe09c74ed000001" // parse body var body objectDivideBody if err := c.ParseJsonBody(req, &body); err != nil { services.Res(res).Error(400, "invalid_body", "request body is invalid or malformed. Expects valid json body") return } // object is required if c.validate.IsEmpty(body.Object) { services.Res(res).Error(400, "missing_parameter", "Missing required field: object") return } // number of objects must be greater than 1 if body.NumObjects < 2 { services.Res(res).Error(400, "invalid_parameter", "num_objects: must be greater than 1") return } // number of objects must not be greater than 100 if body.NumObjects > 100 { services.Res(res).Error(400, "invalid_parameter", "num_objects: must be greater than 1") return } dbTx, err := db.GetPostgresHandleWithRepeatableReadTrans() if err != nil { c.log.Error(err.Error()) services.Res(res).Error(500, "", "server error") return } // get the object object, found, err := models.FindObjectByObjectID(dbTx, body.Object) if !found { dbTx.Rollback() services.Res(res).Error(404, "not_found", "object was not found") return } else if err != nil { dbTx.Rollback() c.log.Error(err.Error()) services.Res(res).Error(500, "", "server error") return } // ensure object is a valuable type if object.Type == models.ObjectValueless { dbTx.Rollback() services.Res(res).Error(400, "invalid_parameter", "object: object must be a valuabe type (obj_value) ") return } // ensure object has enough balance (minimum of 0.000001) if object.Balance < 0.000001 { dbTx.Rollback() services.Res(res).Error(400, "invalid_parameter", "object: object must have a minimum balance of 0.000001") return } // ensure object belongs to authorizing wallet if object.Wallet.ObjectID != authWalletID { dbTx.Rollback() services.Res(res).Error(401, "unauthorized", "object does not belong to authorizing wallet") return } // if meta is provided, ensure it is not greater than the limit size if !body.InheritMeta && !c.validate.IsEmpty(body.Meta) && len([]byte(body.Meta)) > MaxMetaSize { services.Res(res).Error(400, "invalid_meta_size", fmt.Sprintf("Meta contains too much data. Max size is %d bytes", MaxMetaSize)) return } else { if body.InheritMeta { body.Meta = object.Meta } } // calculate new balance per object newBalance := object.Balance / float64(body.NumObjects) // delete object dbTx.Delete(&object) // create new objects newObjects := []models.Object{} for i := 0; i < body.NumObjects; i++ { // generate a pin countryCallCode := config.CurrencyCallCodes[strings.ToUpper(object.Service.Identity.BaseCurrency)] newPin, err := services.NewObjectPin(strconv.Itoa(countryCallCode)) if err != nil { dbTx.Rollback() c.log.Error(err.Error()) services.Res(res).Error(500, "", "server error") return } newObj := NewObject(newPin, models.ObjectValue, object.Service, object.Wallet, newBalance, body.Meta) err = models.CreateObject(dbTx, &newObj) if err != nil { dbTx.Rollback() c.log.Error(err.Error()) services.Res(res).Error(500, "", "server error") return } newObjects = append(newObjects, newObj) } dbTx.Commit() services.Res(res).Json(newObjects) }