├── .gitignore ├── package.json ├── LICENSE.md ├── README.md └── localbitcoins.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "localbitcoins-api", 3 | "version": "0.0.3", 4 | "description": "LocalBitcoins API wrapper for NodeJS", 5 | "keywords": [ 6 | "localbitcoins", 7 | "api", 8 | "btc", 9 | "bitcoin" 10 | ], 11 | "author": "Vlad Nistor (https://github.com/vnistor)", 12 | "contributors": ["Anthony Mayfield (https://github.com/mrmayfield)", "Robert Myers (https://github.com/nothingisdead)"], 13 | "license": "MIT", 14 | "dependencies": { 15 | "querystring": ">=0.2.0", 16 | "request": ">=2.27.0" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/CoinFlux/localbitcoins-api" 21 | }, 22 | "main": "localbitcoins.js", 23 | "engines": { 24 | "node": ">=0.10" 25 | }, 26 | "bugs": { 27 | "url": "https://github.com/CoinFlux/localbitcoins-api/issues", 28 | "email": "contact@coinflux.eu" 29 | }, 30 | "readmeFilename": "README.md" 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 CoinFlux Services SRL 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | LocalBitcoins Node API 2 | =========== 3 | 4 | NodeJS Client Library for the LocalBitcoins API 5 | 6 | This is an asynchronous node js client for the localbitcoins.com API. 7 | 8 | It exposes all the API methods found here: https://localbitcoins.com/api-docs/ through the 'api' method: 9 | 10 | Example Usage: 11 | 12 | `npm install localbitcoins-api` 13 | 14 | ```javascript 15 | var LBCClient = require('localbitcoins-api'); 16 | var lbc = new LBCClient(api_key, api_secret); 17 | 18 | var ad_id; //set to value when applicable 19 | var params = {}; 20 | 21 | 22 | // Display user's info 23 | lbc.api('myself', ad_id, params, function(error, data) { 24 | if(error) { 25 | console.log(error); 26 | } 27 | else { 28 | console.log(data); 29 | } 30 | }); 31 | 32 | ``` 33 | 34 | To-Do: 35 | - Get all methods working 36 | 37 | CHANGELOG: 38 | 0.0.3 - Add /contact_info/ and /contact_info/{contact_id} routes 39 | 40 | Credit: 41 | 42 | Initially inspired by: 43 | https://github.com/nothingisdead/npm-kraken-api. 44 | With contributions from: 45 | https://github.com/mrmayfield/localbitcoins-node. 46 | https://github.com/vnistor/localbitcoins-node. 47 | -------------------------------------------------------------------------------- /localbitcoins.js: -------------------------------------------------------------------------------- 1 | var request = require('request') 2 | var crypto = require('crypto'); 3 | var querystring = require('querystring'); 4 | 5 | 6 | function LBCClient(key, secret, otp) { 7 | var nonce = new Date() * 1000; 8 | var self = this; 9 | 10 | var config = { 11 | url: 'https://localbitcoins.com/api', 12 | key: key, 13 | secret: secret, 14 | otp: otp, 15 | timeoutMS: 5000 16 | }; 17 | 18 | /** 19 | * This method makes a public or private API request. 20 | * @param {String} method The API method (public or private) 21 | * @param {Object} params Arguments to pass to the api call 22 | * @param {Function} callback A callback function to be executed when the request is complete 23 | * @return {Object} The request object 24 | */ 25 | function api(method, ad_id, params, callback) { 26 | var methods = { 27 | onlineAds: ['buy-bitcoins-online'], 28 | public: ['countrycodes'], 29 | private: ['ad-get', 'ad-get/ad_id', 'myself', 'ads', 30 | 'dashboard', 'dashboard/released', 'dashboard/canceled', 'dashboard/closed', 31 | 'dashboard/released/buyer', 'dashboard/canceled/buyer', 'dashboard/closed/buyer', 32 | 'dashboard/released/seller', 'dashboard/canceled/seller', 'dashboard/closed/seller', 33 | 'wallet-send', 'wallet', 'contact_info' 34 | ] 35 | }; 36 | if(methods.public.indexOf(method) !== -1) { 37 | return publicMethod(method, params, ad_id, callback); 38 | } 39 | else if(methods.private.indexOf(method) !== -1) { 40 | return privateMethod(method, params, ad_id, callback); 41 | } 42 | else { 43 | throw new Error(method + ' is not a valid API method.'); 44 | } 45 | } 46 | 47 | /** 48 | * This method makes a public API request. 49 | * @param {String} method The API method (public or private) 50 | * @param {Object} params Arguments to pass to the api call 51 | * @param {Function} callback A callback function to be executed when the request is complete 52 | * @return {Object} The request object 53 | */ 54 | function publicMethod(method, params, ad_id, callback) { 55 | params = params || {}; 56 | 57 | var path; 58 | if (ad_id) { 59 | path = '/' + method + '/' + ad_id; 60 | } else { 61 | path = '/' + method; 62 | } 63 | 64 | var url = config.url + path; 65 | 66 | return rawRequest(url, {}, params, callback); 67 | } 68 | 69 | /** 70 | * This method makes a private API request. 71 | * @param {String} method The API method (public or private) 72 | * @param {Object} params Arguments to pass to the api call 73 | * @param {Function} callback A callback function to be executed when the request is complete 74 | * @return {Object} The request object 75 | */ 76 | function privateMethod(method, params, ad_id, callback) { 77 | params = params || {}; 78 | 79 | var path; 80 | 81 | if (ad_id) { 82 | path = '/' + method + '/' + ad_id; 83 | } else { 84 | path = '/' + method; 85 | } 86 | 87 | var url = config.url + path; 88 | 89 | var signature = getMessageSignature(path, params, nonce); 90 | 91 | var headers = { 92 | 'Content-Type': 'application/x-www-form-urlencoded', 93 | 'Apiauth-Key': config.key, 94 | 'Apiauth-Nonce': nonce, 95 | 'Apiauth-Signature': signature 96 | }; 97 | 98 | return rawRequest(url, headers, params, method, callback); 99 | } 100 | 101 | /** 102 | * This method returns a signature for a request as a Base64-encoded string 103 | * @param {String} path The relative URL path for the request 104 | * @param {Object} request The POST body 105 | * @param {Integer} nonce A unique, incrementing integer 106 | * @return {String} The request signature 107 | */ 108 | function getMessageSignature(path, params, nonce) { 109 | var postParameters = querystring.stringify(params); 110 | var path = '/api' + path + '/'; 111 | var message = nonce + config.key + path + postParameters; 112 | var auth_hash = crypto.createHmac("sha256", config.secret).update(message).digest('hex').toUpperCase(); 113 | return auth_hash; 114 | } 115 | 116 | /** 117 | * This method sends the actual HTTP request 118 | * @param {String} url The URL to make the request 119 | * @param {Object} headers Request headers 120 | * @param {Object} params POST body 121 | * @param {Function} callback A callback function to call when the request is complete 122 | * @return {Object} The request object 123 | */ 124 | function rawRequest(url, headers, params, method, callback) { 125 | 126 | var gets = ['ad-get', 'dashboard', 'dashboard/released', 'dashboard/canceled', 127 | 'dashboard/closed', 'dashboard/released/buyer', 'dashboard/canceled/buyer', 128 | 'dashboard/closed/buyer', 'dashboard/released/seller', 'dashboard/canceled/seller', 129 | 'dashboard/closed/seller', 'wallet', 'contact_info']; 130 | var posts = [ 'ad-get/ad_id', 'myself', 'ads', 131 | 'wallet-send', 'wallet-balance', 'wallet-addr']; 132 | 133 | if (posts.indexOf(method) !== -1) { 134 | 135 | var options = { 136 | url: url + '/', 137 | headers: headers, 138 | form: params, 139 | }; 140 | 141 | var req = request.post(options, function(error, response, body) { 142 | if(typeof callback === 'function') { 143 | var data; 144 | 145 | if(error) { 146 | callback.call(self, new Error('Error in server response: ' + JSON.stringify(error)), null); 147 | return; 148 | } 149 | 150 | try { 151 | data = JSON.parse(body); 152 | } 153 | catch(e) { 154 | callback.call(self, new Error('Could not understand response from server: ' + body), null); 155 | return; 156 | } 157 | 158 | if(data.error && data.error.length) { 159 | callback.call(self, data.error, null); 160 | } 161 | else { 162 | callback.call(self, null, data); 163 | } 164 | } 165 | }); 166 | 167 | return req; 168 | 169 | } else { 170 | 171 | var options = { 172 | url: url + '/', 173 | headers: headers, 174 | }; 175 | 176 | var req = request.get(options, function(error, response, body) { 177 | if(typeof callback === 'function') { 178 | var data; 179 | 180 | if(error) { 181 | callback.call(self, new Error('Error in server response: ' + JSON.stringify(error)), null); 182 | return; 183 | } 184 | 185 | try { 186 | data = JSON.parse(body); 187 | } 188 | catch(e) { 189 | callback.call(self, new Error('Could not understand response from server: ' + body), null); 190 | return; 191 | } 192 | 193 | if(data.error && data.error.length) { 194 | callback.call(self, data.error, null); 195 | } 196 | else { 197 | callback.call(self, null, data); 198 | } 199 | } 200 | }); 201 | 202 | return req; 203 | } 204 | } 205 | 206 | self.api = api; 207 | self.publicMethod = publicMethod; 208 | self.privateMethod = privateMethod; 209 | } 210 | 211 | module.exports = LBCClient; 212 | --------------------------------------------------------------------------------