reward.js

/*
 * Copyright (c) 2018 Zippie Ltd.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

 /**
 * @module zippie-utils/reward
 * 
 * @example
 * // Initialise Reward API
 * reward.init('secure_prefix', 'private_key', 'api_key')
 * 
 * // Create a secure user reference
 * const userRef = reward.getUserReference('dave@example.com')
 * 
 * // Check balance
 * const balance = reward.getUserBalance(userRef, 'reward_token')
 * 
 * // Add to balance
 * reward.rewardTo(userRef, 'reward_token', 'You Received a Reward')
 * 
 */

const axios = require('axios')
const shajs = require('sha.js')

let __uri = 'https://goerli-rewardapi.zippie.org'
let __prefix = ''
let __privateKey = ''
let __apiKey = ''

/**
 * Initialise Reward API with common values
 * @param {String} prefix secure salt
 * @param {*} privateKey 
 * @param {String} apiKey zippie service api key
 * @param {String} uri Reward Service URI
 */
function init(prefix, privateKey, apiKey, uri) {
  __prefix = prefix
  __privateKey = privateKey
  __apiKey = apiKey
  __uri = uri || __uri
}

/**
 * Create a hash digest of a message
 * @private
 * @param {String} message 
 * @returns {String} hex encoded hash
 */
function sha256hash(message) {
  const buf = Buffer.from(message)
  const hash = shajs('sha256').update(buf).digest()

  return hash.toString('hex')
}

/**
 * Converts a local User Id into a secure value suitible for
 * use with the Reward API
 * @param {String} userid local user id
 * @returns {String} hex encoded secure user reference
 */
function getUserReference(userid) {
  return sha256hash(__prefix.toString() + userid.toString())
}

/**
 * Gets the current reward balance for a user
 * @param {String} userRef secure user reference
 * @param {String} token reward token address
 * @returns {String} balance
 */
async function getUserBalance(userRef, token) {
  const response = await axios.post(
    __uri + '/get_user_balance',
    {
      userid: userRef,
      token_address: token
    },
    { headers: { 'Content-Type': 'application/json;charset=UTF-8', 'api-key': __apiKey }}
  )

  if ('error' in response.data) throw response.data.error
  return response.data
}

/**
 * 
 * @param {String} userRef secure user reference
 * @param {String} token reward token address
 */
async function getCheques(userRef, token) {
  const response = await axios.post(
    __uri + '/get_cheques',
    {
      userid: userRef,
      token_address: token
    },
    { headers: { 'Content-Type': 'application/json;charset=UTF-8', 'api-key': __apiKey }}
  )

  if ('error' in response.data) throw response.data.error
  return response.data
}

/**
 * Creates a pending payment with the current balance of a reward token
 * @param {String} userRef secure user reference
 * @param {String} token reward token address
 * @param {String} message display message for transaction
 */
async function createPendingCheque(userRef, token, message) {
  const response = await axios.post(
    __uri + '/create_pending_cheque',
    {
      userid: userRef,
      token_address: token,
      message
    },
    { headers: { 'Content-Type': 'application/json;charset=UTF-8', 'api-key': __apiKey }}
  )

  if ('error' in response.data) throw response.data.error
  return response.data
}

/**
 * Adds an amount to the users pending reward balance
 * @param {String} userRef secure user reference
 * @param {String} token reward token address
 * @param {String} amount token amount to add
 * @param {String} message reward description message, maximum 100 characters
 */
async function rewardTo(userRef, token, amount, message) {
  const intAmount = parseInt(amount)
  const response = await axios.post(
    __uri + '/reward_to',
    {
      userid: userRef,
      token_address: token,
      reward_amount: intAmount,
      reward_message: message
    },
    { headers: { 'Content-Type': 'application/json;charset=UTF-8', 'api-key': __apiKey }}
  )

  if ('error' in response.data) throw response.data.error
  return response.data
}

/**
 * Mark a cheque as claimed
 * @param {String} cheque cheque reference
 */
async function markChequeClaimed(cheque) {
  const response = await axios.post(
    __uri + '/mark_cheque_claimed',
    {
      cheque
    },
    { headers: { 'Content-Type': 'application/json;charset=UTF-8', 'api-key': __apiKey }}
  )

  if ('error' in response.data) throw response.data.error
  return response.data
}

/**
 * 
 * @param {String} userRef secure user reference
 * @param {String} walletAddress users wallet address
 * @param {String} token reward token address
 */
async function registerWallet(userRef, walletAddress, token) {
  const response = await axios.post(
    __uri + '/register_wallet',
    {
      userid: userRef,
      address: walletAddress,
      token_address: token
    },
    { headers: { 'Content-Type': 'application/json;charset=UTF-8', 'api-key': __apiKey }}
  )

  if ('error' in response.data) throw response.data.error
  return response.data
}

/**
 * Creates a unique referral code for a user
 * @param {String} userRef secure user reference
 */
async function createReferralCode(userRef) {
  const response = await axios.post(
    __uri + '/create_referral_code',
    {
      userid:  userRef,
    },
    { headers: { 'Content-Type': 'application/json;charset=UTF-8', 'api-key': __apiKey }}
  )
  if ('error' in response.data) throw response.data.error
  return response.data
}

/**
 * Retrieves a secure user reference for a given referral code
 * @param {String} referral_code referral code generated by createReferralCode
 */
async function getUserIdFromReferralCode(referral_code) {
  const response = await axios.post(
    __uri + '/get_userid_from_referral_code',
    {
      referral_code: referral_code
    },
    { headers: { 'Content-Type': 'application/json;charset=UTF-8', 'api-key': __apiKey }}
  )
  if ('error' in response.data) throw response.data.error
  return response.data
}

/**
 * Gets a value for a stored key
 * @param {String} userRef secure user reference
 * @param {String} key value to retrieve
 */
async function getUserKey(userRef, key) {
  const response = await axios.post(
    __uri + '/get_userid_kv',
    {
      userid: userRef,
      key: key
    },
    { headers: { 'Content-Type': 'application/json;charset=UTF-8', 'api-key': __apiKey }}
  )
  if ('error' in response.data) throw response.data.error
  return response.data
}

/**
 * Sets a Key Value pair for a user
 * @param {String} userRef secure user reference
 * @param {String} key index key
 * @param {String} value value to be stored
 */
async function setUserKey(userRef, key, value) {
  const response = await axios.post(
    __uri + '/set_userid_kv',
    {
      userid: userRef,
      key: key,
      value: value
    },
    { headers: { 'Content-Type': 'application/json;charset=UTF-8', 'api-key': __apiKey }}
  )
  if ('error' in response.data) throw response.data.error
  return response.data
}

/**
 * Create a One Time Password for phone number verification
 * @param {String} phonenumber Phone number to verify
 * @param {String} message Message for the OTP SMS
 * @param {String} referralCode Code for referral
 * @param {String} customer Zippie Project ID
 */
async function createOTP(phonenumber, message, referralCode, customer) {
  const response = await axios.post(
    __uri + '/create_otp',
    {
      phonenumber,
      message,
      referralCode,
      customer
    },
    { headers: { 'Content-Type': 'application/json;charset=UTF-8', 'api-key': __apiKey }}
  )
  if ('error' in response.data) throw response.data.error
  return response.data
}

/**
 * Resends an One Time Password in case of non-delivery
 * @param {String} phonenumber Phone number to verify 
 * @param {String} message Message for the OTP SMS 
 * @param {String} customer Zippie Project ID
 */
async function resendOTP(phonenumber, message, customer) {
  const response = await axios.post(
    __uri + '/resend_otp',
    {
      phonenumber,
      message,
      customer
    },
    { headers: { 'Content-Type': 'application/json;charset=UTF-8', 'api-key': __apiKey }}
  )
  if ('error' in response.data) throw response.data.error
  return response.data
}

/**
 * Submits the One Time Password recieved for verificaiton
 * @param {String} phonenumber Phone number to verify  
 * @param {String} otpCode OTP received by SMS
 * @param {String} customer zippie project id
 */
async function submitOTP(phonenumber, otpCode, customer) {
  const response = await axios.post(
    __uri + '/submit_otp',
    {
      phonenumber,
      otp: otpCode,
      customer
    },
    { headers: { 'Content-Type': 'application/json;charset=UTF-8', 'api-key': __apiKey }}
  )
  if ('error' in response.data) throw response.data.error
  return response.data
}

/**
 * Checks to see if a phone number has passed verification
 * @param {String} phonenumber Phone number to check
 */
async function checkPhoneNumber(phonenumber) {
  const response = await axios.post(
    __uri + '/check_phone_number',
    {
      phonenumber
    },
    { headers: { 'Content-Type': 'application/json;charset=UTF-8', 'api-key': __apiKey }}
  )
  if ('error' in response.data) throw response.data.error
  return response.data
}

/**
 * Set a phone number to claimed
 * @param {String} phonenumber phone number to finish referral process
 */
async function finishReferral(phonenumber) {
  const response = await axios.post(
    __uri + '/finish_referral',
    {
      phonenumber
    },
    { headers: { 'Content-Type': 'application/json;charset=UTF-8', 'api-key': __apiKey }}
  )
  if ('error' in response.data) throw response.data.error
  return response.data
}

/**
 * Queue a reward to be released later
 * this will show up in the users transactions
 * but cannot be used until released or expiry
 * 
 * @param {String} userRef secure user reference
 * @param {String} amount amount to reward
 * @param {String} tokenAddress token to reward
 * @param {String} message message for user
 * @param {Date} expiry when the condition will expire
 */
async function queuePendingReward(userRef, amount, tokenAddress, message, expiry) {
  const response = await axios.post(
    __uri + '/queue_pending_reward',
    {
      userid: userRef,
      reward_amount: amount,
      token_address: tokenAddress,
      reward_message: message,
      expiry
    },
    { headers: { 'Content-Type': 'application/json;charset=UTF-8', 'api-key': __apiKey }}
  )
  if ('error' in response.data) throw response.data.error
  return response.data
}

/**
 * List all the pending rewards for a user
 * 
 * @param {String} userRef secure user reference
 * @param {String} tokenAddress token to list
 */
async function getPendingRewards(userRef, tokenAddress) {
  const response = await axios.post(
    __uri + '/get_pending_rewards',
    {
      userid: userRef,
      tokenAddress
    },
    { headers: { 'Content-Type': 'application/json;charset=UTF-8', 'api-key': __apiKey }}
  )
  if ('error' in response.data) throw response.data.error
  return response.data
}

/**
 * Releases a pending reward allocated with queuePendingReward
 * 
 * @param {String} userRef secure user reference
 * @param {String} rewardId reward identifier
 */
async function releasePendingReward(userRef, rewardId) {
  const response = await axios.post(
    __uri + '/release_pending_reward',
    {
      userid: userRef,
      rewardId
    },
    { headers: { 'Content-Type': 'application/json;charset=UTF-8', 'api-key': __apiKey }}
  )
  if ('error' in response.data) throw response.data.error
  return response.data
}

/**
 * Cancels a pending reward allocated with queuePendingReward
 * 
 * @param {String} userRef secure user reference
 * @param {String} rewardId reward identifier
 */
async function cancelPendingReward(userRef, rewardId) {
  const response = await axios.post(
    __uri + '/cancel_pending_reward',
    {
      userid: userRef,
      rewardId
    },
    { headers: { 'Content-Type': 'application/json;charset=UTF-8', 'api-key': __apiKey }}
  )
  if ('error' in response.data) throw response.data.error
  return response.data
}

/**
 * Send generic event to reward api
 * Events and actions can be defined in the rewards dashboard
 * @param {JSON} jsonData JSON Encoded event data
 */
async function sendEvent(jsonData) {
  const response = await axios.post(
    __uri + '/event',
  jsonData,
    { headers: { 'Content-Type': 'application/json;charset=UTF-8', 'api-key': __apiKey }}
  )
  if ('error' in response.data) throw response.data.error
  return response.data
}

module.exports = {
  init,
  getUserReference,
  sendEvent,
  getUserBalance,
  getCheques,
  createPendingCheque,
  rewardTo,
  markChequeClaimed,
  registerWallet,
  createReferralCode,
  getUserIdFromReferralCode,
  getUserKey,
  setUserKey,
  createOTP,
  resendOTP,
  submitOTP,
  checkPhoneNumber,
  finishReferral,
  queuePendingReward,
  getPendingRewards,
  releasePendingReward,
  cancelPendingReward
}