Push Notification

Push notification is a powerful tool most mobile web apps are missing. Blocto helps you bridge the gap and helps you push on demand.

Domains

Environment

Domain

Staging

api-staging.blocto.app

Production

api.blocto.app

Authorization: <JWT>
Content-Type: application/json

APIs

post
Send push notification

https://api.blocto.app/notification/send
Send push notification to users by either user IDs or tags.
Request
Response
Request
Body Parameters
text
required
string
the message shown on the push notification
url
required
string
the URL to direct users to
tags
optional
array
user tags to send push notification to
user_ids
optional
array
user IDs to send push notification to
Response
200: OK
{
'message_code': ok
}

get
Tag users by user ID

https://api.blocto.app/notification/tag
Set tag to a group of users for future push.
Request
Response
Request
Body Parameters
tag
required
string
user tag
user_ids
required
array
user IDs to set tag
Response
200: OK
{
'message_code': ok
}

Getting User IDs From your Web App

Blocto injects a global API into websites at window.bloctoProvider and window.{network} at the same time. Currently, Blocto supports networks:

  • ethereum

  • tron

  • tangerine

Register Push Notification

let web3 = new Web3(window.ethereum) // or window.tangerine or window.bloctoProvider
if (web3.currentProvider.isBlocto) {
try {
let encryptedUserId = await web3.currentProvider.registerPushNotification("{appId}")
// send encryptedUserId to your backend
} catch(e) {
// error handling
// e.mesage: see the below
}
}

Error Handling

  • blocked: user clicked the block button.

  • cancelled: user clicked the cancel button.

  • noNetworkConnection: no network connection.

  • appIdNotExist: your app id doesn't exist.

  • internal: wallet app internal error. Please contact us.

Decrypt User ID

JavaScript
Golang
JavaScript
var aesjs = require('aes-js');
// The aes key provided by blocto, and make sure it is stored in somewhere safe.
var key = aesjs.utils.hex.toBytes('71500f4803c54fcf9445a09e3434afce7552d9916bce4480e54c48a246be3f05');
// The encrypted data from blocto web3 provider.
var ivAndEncryptedData = aesjs.utils.hex.toBytes('3b28cd5be9629b777392e968fef4b0e9ea2715b4f9966ddbfa4dab41b6468a0c30476c6d1e4f58d1b22c9877eaf8bf626dc4f3f10932208684235927b28a2cd85d5aea5595fcd0618019fe80a1afd35c');
var iv = ivAndEncryptedData.slice(0, 16);
var encryptedBytes = ivAndEncryptedData.slice(16);
var aesCbc = new aesjs.ModeOfOperation.cbc(key, iv);
var paddedDecryptedBytes = aesCbc.decrypt(encryptedBytes);
let res = unpad(paddedDecryptedBytes);
if (res.err) {
throw res.err
}
console.log(JSON.parse(ab2str(res.unpaddedBytes)));
// `{ user_id: '79efdb10-64dd-436a-9ec9-6bfda8c36e1d' }` is expected
// Store the `user_id` and make some mapping according to your application.
function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint16Array(buf));
}
function unpad(decryptedBytes) {
let paddedLen = decryptedBytes.length;
if (paddedLen === 0) {
return {
unpaddedBytes: null,
err: new Error('invalid padding size'),
}
}
let padLen = decryptedBytes[paddedLen - 1];
if (padLen > paddedLen || padLen > 16) {
return {
unpaddedBytes: null,
err: new Error('invalid padding size'),
}
}
for (let i = paddedLen - padLen; i < decryptedBytes.length; i++) {
if (decryptedBytes[i] !== padLen) {
return {
unpaddedBytes: null,
err: new Error('invalid padding'),
}
}
}
return {
unpaddedBytes: decryptedBytes.slice(0, paddedLen - padLen),
err: null,
}
}
Golang
package main
import (
"crypto/aes"
"crypto/cipher"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
)
func main() {
// The aes key provided by blocto, and make sure it is stored in somewhere safe.
key, err := hex.DecodeString("71500f4803c54fcf9445a09e3434afce7552d9916bce4480e54c48a246be3f05")
if err != nil {
panic(err)
}
// The encrypted data from blocto web3 provider.
encryptedData, err := hex.DecodeString("3b28cd5be9629b777392e968fef4b0e9ea2715b4f9966ddbfa4dab41b6468a0c30476c6d1e4f58d1b22c9877eaf8bf626dc4f3f10932208684235927b28a2cd85d5aea5595fcd0618019fe80a1afd35c")
if err != nil {
panic(err)
}
decryptedData, err := CBCDecrypt(key, encryptedData)
if err != nil {
panic(err)
}
type data struct {
UserID string `json:"user_id"`
}
d := &data{}
err = json.Unmarshal(decryptedData, d)
if err != nil {
panic(err)
}
fmt.Println(d.UserID)
// `79efdb10-64dd-436a-9ec9-6bfda8c36e1d` is expected
// Store the `user_id` and make some mapping according to your application.
}
// Key stands for a AES key.
type Key []byte
func CBCDecrypt(key Key, cipherBytes []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, fmt.Errorf("failed to create new aes cipher with key [%s]. err(%s)",
key, err)
}
if len(cipherBytes) < aes.BlockSize {
return nil, errors.New("cipher bytes block size is too short")
}
// Use the beginning of the cipher as the initialization vector.
iv := cipherBytes[:aes.BlockSize]
cipherBytes = cipherBytes[aes.BlockSize:]
// Decrypt data in CBC mode.
mode := cipher.NewCBCDecrypter(block, iv)
decrypted := make([]byte, len(cipherBytes))
mode.CryptBlocks(decrypted, cipherBytes)
unpadded, err := unpad(decrypted)
if err != nil {
return nil, fmt.Errorf("failed to unpad data. err(%s)", err)
}
return unpadded, nil
}
// unpad removes the padding of a given byte array, according to the same rules
// as described in the pad function.
func unpad(padded []byte) ([]byte, error) {
paddedLen := len(padded)
if paddedLen == 0 {
return nil, errors.New("invalid padding size")
}
pad := padded[paddedLen-1]
padLen := int(pad)
if padLen > paddedLen || padLen > aes.BlockSize {
return nil, errors.New("invalid padding size")
}
for _, v := range padded[paddedLen-padLen : paddedLen-1] {
if v != pad {
return nil, errors.New("invalid padding")
}
}
return padded[:paddedLen-padLen], nil
}