'use strict'; var pg = require('pg'); var logger = require('./logger'); var PG_ERRORS = { 23505: 'uniqueViolation' }; var client = null; exports.init = function init(conString) { if (client !== null) return; if (!conString) { throw new Error('Postgres connection string is required'); } client = new pg.Client(conString); client.on('error', function (err) { logger.error(err); }); client.connect(); }; exports.recordBill = function recordBill(deviceFingerprint, rec, cb) { client.query('INSERT INTO bills (device_fingerprint, denomination, currency_code, ' + 'satoshis, to_address, transaction_id, device_time) ' + 'VALUES ($1, $2, $3, $4, $5, $6, $7)', [deviceFingerprint, rec.fiat, rec.currency, rec.satoshis, rec.toAddress, rec.txId, rec.deviceTime], cb); }; exports.recordDeviceEvent = function recordDeviceEvent(deviceFingerprint, event, cb) { client.query('INSERT INTO device_events (device_fingerprint, event_type, note, device_time)' + 'VALUES ($1, $2, $3, $4)', [deviceFingerprint, event.eventType, event.note, event.deviceTime], cb); }; // each received "partial transaction" contains sum of all previous bills // (vel. no need to do any server-side summing) function updatePartialTransaction(values, cb) { var values2 = [ values[4], values[6], values[0], 'partial' ]; client.query('UPDATE transactions SET ' + 'satoshis=$1, ' + 'fiat=$2 ' + 'WHERE id=$3 AND status=$4', values2, cb); } function fetchTransaction(txId, cb) { client.query('SELECT status, tx_hash, error FROM transactions WHERE id=$1', [txId], function (err, results) { if (err) return cb(err); // This should never happen, since we already checked for existence if (results.rows.length === 0) return cb(new Error('Couldn\'t find transaction.')); var result = results.rows[0]; cb(null, {txHash: result.tx_hash, err: result.error, status: result.status}); }); } exports.summonTransaction = function summonTransaction(deviceFingerprint, tx, cb) { var status = tx.status || 'pending'; var values = [ tx.txId, status, deviceFingerprint, tx.toAddress, tx.satoshis, tx.currencyCode, tx.fiat ]; // First attampt an INSERT // If it worked, go ahead with transaction // If duplicate and partial update with new bills // If duplicate, but not partial fetch status and return client.query('INSERT INTO transactions ' + '(id, status, device_fingerprint, to_address, satoshis, currency_code, fiat) ' + 'VALUES ($1, $2, $3, $4, $5, $6, $7)', values, function(err) { if (err) { if (PG_ERRORS[err.code] === 'uniqueViolation') { if (status === 'partial') return updatePartialTransaction(values, cb); return fetchTransaction(tx.txId, cb); } return cb(err); } cb(); }); }; exports.reportTransactionError = function reportTransactionError(tx, errString, status) { client.query('UPDATE transactions SET status=$1, error=$2 WHERE id=$3', [status, errString, tx.txId]); }; exports.completeTransaction = function completeTransaction(tx, txHash) { if (txHash) client.query('UPDATE transactions SET tx_hash=$1, status=$2, completed=now() WHERE id=$3', [txHash, 'completed', tx.txId]); else client.query('UPDATE transactions SET status=$1, error=$2 WHERE id=$3', ['failed', 'No txHash received', tx.txId]); };