machinomy
Version:
Micropayments powered by Ethereum
204 lines • 8.8 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
const MigrateOption_1 = require("./MigrateOption");
const BigNumber = require("bignumber.js");
const payment_1 = require("./payment");
const accept_payment_request_1 = require("./accept_payment_request");
const Registry_1 = require("./Registry");
const MachinomyOptions_1 = require("./MachinomyOptions");
const decko_1 = require("decko");
/**
* Machinomy is a library for micropayments in Ether over HTTP.
* Machinomy provides API to send and receive a minuscule amount of money instantly.
* Core method is [buy]{@link Machinomy.buy}. The method does all the heavy lifting providing an easy interface
* for micropayments.
*
* See [examples](https://github.com/machinomy/machinomy/tree/master/examples) directory for both client and server sides.
*
* NB. All monetary values below are denominated in Wei, including: [buy]{@link Machinomy.buy} and
* [deposit]{@link Machinomy.deposit} methods.
*/
class Machinomy {
constructor(account, web3, options) {
this.migrated = false;
this.account = account;
let _options = MachinomyOptions_1.default.defaults(options);
this.registry = new Registry_1.default(account, web3, _options);
}
/**
* Entrypoint for a purchasing.
*
* Wnen you `buy` for the first time from the same receiver, the method opens a channel with a deposit equal to `price`✕10.
* Next method call forms a payment and sends it via http to `gateway` url.
*
* The method then returns a token and channel id, in form of {@link BuyResult}.
*
* @example
* <pre><code>machinomy.buy({
* receiver: receiver, // address
* price: 100,
* gateway: 'http://localhost:3001/machinomy'
* })
* </code></pre>
*/
async buy(options) {
await this.checkMigrationsState();
if (!options.gateway) {
throw new Error('gateway must be specified.');
}
let client = await this.registry.client();
let channelManager = await this.registry.channelManager();
const payment = await this.nextPayment(options);
const res = await client.doPayment(payment, options.gateway, options.purchaseMeta);
await channelManager.spendChannel(payment, res.token);
return { token: res.token, channelId: payment.channelId };
}
async payment(options) {
await this.checkMigrationsState();
let channelManager = await this.registry.channelManager();
const payment = await this.nextPayment(options);
await channelManager.spendChannel(payment);
return { payment: payment_1.PaymentSerde.instance.serialize(payment) };
}
async pry(uri, datetime) {
await this.checkMigrationsState();
let client = await this.registry.client();
return client.doPreflight(this.account, uri, datetime);
}
async buyUrl(uri) {
await this.checkMigrationsState();
let client = await this.registry.client();
let req = await client.doPreflight(this.account, uri);
return this.buy({
receiver: req.receiver,
price: req.price,
gateway: req.gateway,
meta: req.meta,
tokenContract: req.tokenContract
});
}
/**
* Put more money into the channel.
*
* @example
* <pre><code>
* let channelId = '0x0bf080aeb3ed7ea6f9174d804bd242f0b31ff1ea24800344abb580cd87f61ca7'
* machinomy.deposit(channelId, web3.toWei(1, "ether").toNumber(())) // Put 1 Ether more
* </code></pre>
*
* @param channelId - Channel id.
* @param value - Size of deposit in Wei.
*/
async deposit(channelId, value) {
await this.checkMigrationsState();
const _value = new BigNumber.BigNumber(value);
let channelManager = await this.registry.channelManager();
return channelManager.deposit(channelId, _value);
}
async open(receiver, value, channelId, tokenContract) {
await this.checkMigrationsState();
const _value = new BigNumber.BigNumber(value);
let channelManager = await this.registry.channelManager();
return channelManager.openChannel(this.account, receiver, _value, new BigNumber.BigNumber(0), channelId, tokenContract);
}
/**
* Returns the list of opened channels.
*/
async channels() {
await this.checkMigrationsState();
let channelManager = await this.registry.channelManager();
return channelManager.channels();
}
async openChannels() {
await this.checkMigrationsState();
let channelManager = await this.registry.channelManager();
return channelManager.openChannels();
}
async settlingChannels() {
await this.checkMigrationsState();
let channelManager = await this.registry.channelManager();
return channelManager.settlingChannels();
}
async channelById(channelId) {
await this.checkMigrationsState();
let channelManager = await this.registry.channelManager();
return channelManager.channelById(channelId);
}
/**
* Share the money between sender and reciver according to payments made.
*
* For example a channel was opened with 10 Ether. Sender makes 6 purchases, 1 Ether each.
* Total value transferred is 6 Ether.
* If a party closes the channel, the money deposited to the channel are split.
* The receiver gets 6 Ether. 4 unspent Ethers return to the sender.
*
* A channel can be closed in two ways, according to what party initiates that.
* The method nicely abstracts over that, so you do not need to know what is really going on under the hood.
* For more details on how payment channels work refer to a website.
*/
async close(channelId) {
await this.checkMigrationsState();
let channelManager = await this.registry.channelManager();
return channelManager.closeChannel(channelId);
}
/**
* Save payment into the storage and return an id of the payment. The id can be used by {@link Machinomy.paymentById}.
*/
async acceptPayment(req) {
await this.checkMigrationsState();
let client = await this.registry.client();
return client.acceptPayment(accept_payment_request_1.AcceptPaymentRequestSerde.instance.deserialize(req));
}
/**
* Return information about the payment by id.
*/
async paymentById(id) {
await this.checkMigrationsState();
let storage = await this.registry.storage();
return storage.paymentsDatabase.findByToken(id);
}
async acceptToken(req) {
await this.checkMigrationsState();
let client = await this.registry.client();
return client.acceptVerify(req);
}
async shutdown() {
await this.checkMigrationsState();
let storage = await this.registry.storage();
return storage.engine.close();
}
async nextPayment(options) {
await this.checkMigrationsState();
const price = new BigNumber.BigNumber(options.price);
let channelManager = await this.registry.channelManager();
const channel = await channelManager.requireOpenChannel(this.account, options.receiver, price, undefined, options.tokenContract);
return channelManager.nextPayment(channel.channelId, price, options.meta || '');
}
async checkMigrationsState() {
if (this.migrated)
return;
let storage = await this.registry.storage();
let isLatest = await storage.migrator.isLatest();
let needMigration = !isLatest;
if (needMigration) {
if (this.registry.options.migrate === undefined || this.registry.options.migrate === MigrateOption_1.MigrateOption.Silent) {
this.migrated = true;
return storage.migrator.sync();
}
else {
throw new Error('There are non-applied db-migrations!');
}
}
}
}
__decorate([
decko_1.memoize
], Machinomy.prototype, "checkMigrationsState", null);
exports.default = Machinomy;
//# sourceMappingURL=Machinomy.js.map