Git Product home page Git Product logo

dash-merchant-services's Introduction

dash-payment-webhooks

Get webhooks when your address is paid, duh!

Pre-Requisites

You'll need dashd already.

See dashhive/dashd-installer#4.

Setup

rsync -avhP example.env .env
rsync -avhP ./dashcore-node.example.json ./dashcore-node.json
npm ci
npm run start

API

POST /api/webhooks
Bearer my-api-access-token
{
    url: "https://user@my-super-long-token:example.com/api/dash/payment-received",
    address: "Xxxxxxxxxxxxxxxxxxx",
}

{
    url: "https://user@my-s*************en:example.com/api/dash/payment-received",
    address: "Xxxxxxxxxxxxxxxxxxx",
}

To create a webhook token scoped to certain allowed hostnames:

node ./bin/gentoken.js example.com,example.net

Then give the dwh_ part to the customer, and save the line in ./tokens.json.

dash-merchant-services's People

Contributors

coolaj86 avatar

Watchers

 avatar Rion Daniel Gull avatar

dash-merchant-services's Issues

[doc] How to add `dashd` to systemd

add-dashd.sh:

#!/bin/bash
set -e
set -u

sudo env PATH="${PATH}" \
    serviceman add --system --username "$(whoami)" --path "${PATH}" --name dashd -- \
    dashd -- -conf=/home/app/.dashcore/dash.conf -datadir=/mnt/volume_sfo2_02/dashcore/

add-dashtestd.sh:

#!/bin/bash
set -e
set -u

sudo env PATH="${PATH}" \
    serviceman add --system --username "$(whoami)" --path "${PATH}" --name dashtestd -- \
    dashd -- -conf=/home/app/.dashcore/dashtest.conf -datadir=/mnt/volume_sfo2_03/dashtest/

Path to Announcing Dash Merchant Services

  • Update name on UI (Dash Merchant Services, not Hello API)
  • Show user's token
  • Move to own server with own DB and real domain
  • Move common bits to dash-checkout
  • Test Hello API against Merchant Services
  • Implement payment reminder webhook (i.e. hit soft_quota, nearing expiration, etc)
  • Check RPC for getaddressbalance
  • Announce

Dash DayPass

  • Initialize script with a given Public Address
  • Add Webhook for Browser Notification
  • Return txid (and save in localStorage)
  • Expose outputs of txid via rpc
  • Check if txid contains the expected output within a given time, continue
  • Serve JS widget for this stuff

[doc] How to use ZMQ (ZeroMQ) directly

ZeroMQ.js@v6 (beta-not-beta)

To-Dos:

"use strict";

let Zmq = require("zeromq");

async function run() {
    let zmqUrl = "tcp://10.11.5.104:28332";
    //let zmqUrl = "tcp://10.11.5.1041:28332";
    //let zmqUrl = "tcp://10.11.5.104:28331";

    // replaces `let zmqSubSocket = zmq.socket("sub");`
    let sock = new Zmq.Subscriber();

    console.log(`ZeroMQ Subscriber connecting to '${zmqUrl}'`);
    void monitor(sock, onEvent);

    sock.connect(zmqUrl);
    console.log("[connect]");

    sock.subscribe("rawtxlock");
    console.log("[subscribe]");

    // receive() is in c++ land
    // https://github.com/zeromq/zeromq.js/blob/62f6e252f530ea05c86be15b06a58214eac1b34d/src/socket.cc#L307
    // asyncInterator is generic generated by TypeScript
    // https://github.com/zeromq/zeromq.js/blob/62f6e252f530ea05c86be15b06a58214eac1b34d/src/index.ts#L291C1-L292C1
    //console.log(sock);

    /*
    try {
        for await (let [topic, msg] of sock) {
            console.info(`${topic}:`);
            let hex = msg.toString("hex");
            console.info(hex);
        }
    } catch (e) {
        throw e;
    }
    */

    for (;;) {
        let [topic, msg] = await sock.receive().catch(errorToMessage);
        if (topic.length === 0) {
            // intentionally closed
            break;
        }
        if (topic === "error") {
            console.warn("[error] socket failed to receive", msg);
            continue;
        }

        console.info(`[topic] ${topic}:`);
        let hex = msg.toString("hex");
        console.info(hex);
    }

    function errorToMessage(e) {
        if (sock.closed) {
            if (e.code === "EAGAIN") {
                return ["", null];
            }
        }
        return ["error", e];
    }
}

async function onEvent(type, value, address, err) {
    if (err) {
        console.error(err.stack || err);
        return;
    }

    let subject = `[monitor] ${type}@${address}`;
    if (!value) {
        console.info(`${subject}:`);
        return;
    }

    console.info(`${subject}`);
    console.info(value);
}

// events
// https://github.com/zeromq/zeromq.js/blob/v6.0.0-beta.17/src/observer.cc#L15
let events = {
    errors: [
        "accept:error",
        "bind:error",
        "close:error",
        "handshake:error:auth",
        "handshake:error:other",
        "handshake:error:protocol",
    ],
    intervals: ["connect:retry"],
    notifiers: [
        "handshake",
        "connect",
        "connect:delay",
        "accept",
        "bind",
        "close",
        "disconnect",
        "end",
    ],
};

async function monitor(sock, cb) {
    for (;;) {
        let event;
        try {
            event = await sock.events.receive();
        } catch (e) {
            if (!sock.closed) {
                console.error("[error] monitor failed", e);
            }
            break;
        }

        let isError = events.errors.includes(event.type);
        if (isError) {
            let value = event.error?.errno || 0;
            void cb(event.type, value, event.address, event.error);
            continue;
        }

        let hasIntervalValue = events.intervals.includes(event.type);
        if (hasIntervalValue) {
            let value = event.interval;
            void cb(event.type, value, event.address, event.error);
            continue;
        }

        let isNotifier = events.notifiers.includes(event.type);
        if (isNotifier) {
            let value = null;
            void cb(event.type, value, event.address, event.error);
            continue;
        }

        console.warn(`[WARN] unknown event '${event.type}'`);
    }
}

run();

Notes:

ZeroMQ.js@v5 (deprecated-not-deprecated)

  1. Must have BOTH monitor and connect!
  2. These messages can be parsed with dashtx (npm install --save dashtx) and dashtx-inspect (npm install --location=global dashtx)
"use strict";

let Fs = require("node:fs/promises");

// TODO v6-beta
let zmq = require("zeromq");

// TODO read from JSON config
let config = {
  zmq: "tcp://10.11.5.101:28332",
};

async function main() {
  let zmqSubSocket = zmq.socket("sub");

  console.info(`[INFO] ZMQ opening socket to '${config.zmq}'`);

  // workaround for https://github.com/zeromq/zeromq.js/issues/574
  var timeout = setTimeout(function () {
    // neither accepting nor rejecting - either invalid or firewalled (DROP)
    console.error(`ZMQ address '${config.zmq}' cannot be reached`);
    process.exit(1);
  }, 7 * 1000);

  zmqSubSocket.on("connect", function (fd, endPoint) {
    console.info("ZMQ connected to:", endPoint);
    clearTimeout(timeout);
    timeout = null;
  });

  zmqSubSocket.on("connect_delay", function (fd, endPoint) {
    console.warn("ZMQ connection delay:", endPoint);
    clearTimeout(timeout);
    timeout = null;
  });

  zmqSubSocket.subscribe("rawtxlock");
  zmqSubSocket.on("message", function (topic, message) {
    var topicString = topic.toString("utf8");
    console.log("[ZMQ]", topicString, message.toString("hex"));
  });

  zmqSubSocket.on("monitor_error", function (err) {
    console.warn(
      "Error in monitoring: %s, will restart monitoring in 5 seconds",
      err,
    );
    setTimeout(function () {
      zmqSubSocket.monitor(500, 0);
    }, 5000);
  });

  // IMPORTANT: monitor MUST run for "connect" messages
  zmqSubSocket.monitor(500, 0);
  zmqSubSocket.connect(config.zmq);
}

main().catch(function (err) {
  console.error(err.stack);
  process.exit(1);
});

[doc] How to use dashd-rpc directly

"use strict";

let Fs = require("node:fs/promises");

let RpcClient = require("@dashevo/dashd-rpc/promise");

// TODO read from JSON config
let config = {
  protocol: "http",
  user: "app",
  pass: "4472bd70ac35b572d768478266e31312",
  host: "10.11.5.101",
  // mainnet=9998, testnet=19998, regtest=19898
  port: 9998,
};

const E_RPC_IN_WARMUP = -28;

async function main() {
  let file = process.argv[2];
  let rpc = new RpcClient(config);

  let ready = await rpc.getBestBlockHash().catch(function (e) {
    if (e.code === E_RPC_IN_WARMUP) {
      console.info("[INFO] RPC is connected.");
      console.warn(`[WARN] RPC is not fully synced: ${e.message}`);
      return;
    }

    throw e;
  });
  if (ready) {
    console.info("[INFO] RPC is ready.");
  }

  let addrs = [
    "XvZy1npHNGeYHCuDnB6upKqdf6XU9q94qu",
    "Xr5ZkoaLAeoFw7GGiNvuwTYZFeDcM9knMg",
  ];
  if (file) {
    let text = await Fs.readFile(file, "utf8");
    let lines = text.split(/["',\s\r\n]/).filter(Boolean);
    addrs = addrs.concat(lines);
  }

  await getUtxos(rpc, addrs);
}

async function getUtxos(rpc, addresses) {
  // getaddressbalance
  // getaddressutxos
  let ret = await rpc.getAddressUtxos({
    addresses: addresses,
  });

  console.log(
    `${addresses.length} addresses with ${ret.result.length} UTXOs between them`,
  );
}

main().catch(function (err) {
  console.error(err.stack);
  process.exit(1);
});

[doc] How to view logs

Assuming the services are added with serviceman as documented:

sudo journalctl -xefu dashd
sudo journalctl -xefu dash-merchant-services

TODO:

  • ./bin/logs-show-dashd
  • ./bin/logs-show-dash-merchant-services

Wallet Stuff

  • Generate Addresses
  • Generate real embeddable QR Codes
  • Can we lift the wallet from the big lib into a little one

Q3 Roadmap 2023

  • Are XPubs safe? Or aren't they?
  • XPub (or Wallet Phrase) per customer (Depth 4 XPub is sufficient)
  • Depth 3 or Depth 4 XPubs?
    • depth 3 is for internal Account auditing
    • depth 4 is for sharing with Contacts
  • swap in new ZeroMQ and new RPC

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.