├── .gitignore ├── .DS_Store ├── lib ├── .DS_Store ├── Response │ ├── Err.js │ ├── Success.js │ └── Abstract.js ├── Card.js ├── Signature │ ├── Abstract.js │ ├── Widget.js │ └── Pingback.js ├── Environment.js ├── Base.js ├── HttpAction.js ├── Onetimetoken.js ├── Config.js ├── Product.js ├── ApiObject.js ├── Charge.js ├── Subscription.js ├── Widget.js └── Pingback.js ├── features ├── .DS_Store ├── support │ ├── env.js │ ├── hooks.js │ └── common.js ├── step_definitions │ ├── pingback.js │ ├── charge.js │ └── widget.js ├── charge.feature ├── widget.feature └── pingback.feature ├── CHANGELOG.md ├── index.js ├── package.json ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | test.js 3 | npm-debug.log -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/html/paymentwall-node/master/.DS_Store -------------------------------------------------------------------------------- /lib/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/html/paymentwall-node/master/lib/.DS_Store -------------------------------------------------------------------------------- /features/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/html/paymentwall-node/master/features/.DS_Store -------------------------------------------------------------------------------- /features/support/env.js: -------------------------------------------------------------------------------- 1 | var configure = function () { 2 | this.setDefaultTimeout(60 * 1000); 3 | }; 4 | 5 | module.exports = configure; -------------------------------------------------------------------------------- /lib/Response/Err.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.getParameter = function(parameter, JSON_chunk){ 4 | return JSON_chunk[parameter]; 5 | }; -------------------------------------------------------------------------------- /lib/Response/Success.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.getParameter = function(parameter, JSON_chunk){ 4 | return JSON_chunk[parameter]; 5 | }; -------------------------------------------------------------------------------- /lib/Card.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function Card(number,exp_month,exp_year,cvv){ 4 | this.number = number || null, 5 | this.exp_month = exp_month || null, 6 | this.exp_year = exp_year || null, 7 | this.cvv = cvv || null 8 | } 9 | 10 | module.exports = Card; -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | ## [v2.0.0] - 2016-12-9 3 | Changed structure 4 | ### Added 5 | - Response Folder 6 | - Signature Folder 7 | - ApiObject File 8 | - Environment File 9 | - Card File 10 | - HttpAction File 11 | - Config File 12 | ### Changed 13 | - Brick File 14 | - Pingback File 15 | - Widget File 16 | - Subscription File 17 | - Base File 18 | -------------------------------------------------------------------------------- /features/support/hooks.js: -------------------------------------------------------------------------------- 1 | module.exports = function() { 2 | this.Before(function(scenario,callback) { 3 | require('chai').should(); 4 | this.expect = require('chai').expect; 5 | var Paymentwall = require (process.cwd() + '/index'); 6 | 7 | Paymentwall.Configure( 8 | null, 9 | null, 10 | null 11 | ); 12 | 13 | this.paymentwall = Paymentwall; 14 | callback(); 15 | }); 16 | } -------------------------------------------------------------------------------- /lib/Signature/Abstract.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var querystring = require('querystring'); 3 | exports.sortObject = function (o) { 4 | var baseString = ""; 5 | if (typeof(o)=== 'object') { 6 | Object.keys(o).sort().forEach(function(key,i) { 7 | if (typeof(o[key])=== 'object') { 8 | Object.keys(o[key]).sort().forEach(function(sub_key,sub_i) { 9 | var value = o[key][sub_key] || ""; 10 | baseString += key + '['+sub_key+']'+'=' + value; 11 | }); 12 | } else { 13 | var value = o[key] || ""; 14 | 15 | baseString += key + '=' + value; 16 | } 17 | }); 18 | } 19 | return baseString; 20 | } -------------------------------------------------------------------------------- /lib/Environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // check the runtime environment 4 | exports.checkRuntimeEnv = function() { 5 | var runtimeEnv; 6 | if (typeof (process) !== 'undefined' && process.versions) { 7 | if (process.versions.nw) { 8 | runtimeEnv = 'nw'; 9 | } else if (process.versions.node) { 10 | runtimeEnv = 'node'; 11 | }; 12 | }; 13 | if (!runtimeEnv && typeof (window) !== 'undefined' && 14 | window.window === window) { 15 | runtimeEnv = 'browser'; 16 | console.log('Browser is deprecated for this library'); 17 | }; 18 | if (!runtimeEnv) { 19 | throw new Error('unknown runtime environment'); 20 | }; 21 | return runtimeEnv; 22 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports.Base = require('./lib/Base'); 4 | module.exports.Product = require('./lib/Product'); 5 | module.exports.Widget = require('./lib/Widget'); 6 | module.exports.Pingback = require('./lib/Pingback'); 7 | module.exports.Onetimetoken = require('./lib/Onetimetoken'); 8 | module.exports.Charge = require('./lib/Charge'); 9 | module.exports.Subscription = require('./lib/Subscription'); 10 | module.exports.Configure = require('./lib/Config'); 11 | module.exports.WidgetSignature = require('./lib/Signature/Widget'); 12 | module.exports.PingbackSignature = require('./lib/Signature/Pingback'); 13 | 14 | var Environment = require('./lib/Environment'); 15 | var runtime = Environment.checkRuntimeEnv(); 16 | -------------------------------------------------------------------------------- /features/support/common.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | 3 | module.exports = function() { 4 | this.Given(/^Public key "([^"]*)"$/, function (arg1, callback) { 5 | this.paymentwall.Configure.prototype.appKey = arg1; 6 | callback(); 7 | }); 8 | 9 | this.Given(/^Private key "([^"]*)"$/, function (arg1, callback) { 10 | this.paymentwall.Configure.prototype.secretKey = arg1; 11 | callback(); 12 | }); 13 | 14 | this.Given(/^Secret key "([^"]*)"$/, function (arg1, callback) { 15 | this.paymentwall.Configure.prototype.secretKey = arg1; 16 | callback(); 17 | }); 18 | 19 | this.Given(/^API type "([^"]*)"$/, function (arg1, callback) { 20 | this.paymentwall.Configure.prototype.apiType = Number(arg1); 21 | callback(); 22 | }); 23 | } -------------------------------------------------------------------------------- /lib/Signature/Widget.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Abstract = require('./Abstract'); 4 | var crypto = require('crypto'); 5 | 6 | exports.calculateSignature = function (params, secret, version) { 7 | var baseString = ''; 8 | 9 | if (version === 1) { 10 | // TODO: throw exception if no uid parameter is present 11 | baseString += params['uid'] || ''; 12 | baseString += secret; 13 | 14 | return crypto.createHash('md5').update(baseString).digest('hex'); 15 | 16 | } else { 17 | 18 | if (params instanceof Object) { 19 | baseString = Abstract.sortObject(params); 20 | } 21 | 22 | baseString += secret; 23 | var algorithm = (version == 2) ? 'md5' : 'sha256'; 24 | 25 | var shasum = crypto.createHash(algorithm).update(baseString, "utf8"); 26 | 27 | return shasum.digest('hex'); 28 | } 29 | } -------------------------------------------------------------------------------- /lib/Base.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var util = require('util'); 4 | var Config = require('./Config'); 5 | 6 | Base.API_VC = 1; 7 | Base.API_GOODS = 2; 8 | Base.API_CART = 3; 9 | 10 | function Base() { 11 | } 12 | 13 | util.inherits(Base, Config); 14 | 15 | util._extend(Base.prototype, { 16 | 17 | getApiType: function() { 18 | return this.apiType; 19 | }, 20 | 21 | getAppKey: function() { 22 | return this.appKey; 23 | }, 24 | 25 | getSecretKey: function() { 26 | return this.secretKey; 27 | }, 28 | 29 | objectMerge: function(a, b) { 30 | for (var x in b) a[x] = b[x]; 31 | return a; 32 | }, 33 | 34 | appendToErrors: function(err) { 35 | return this.errors.push(err); 36 | }, 37 | 38 | getErrors: function() { 39 | return this.errors; 40 | }, 41 | 42 | getErrorSummary: function() { 43 | return this.getErrors().join("\n"); 44 | } 45 | }); 46 | module.exports = Base; -------------------------------------------------------------------------------- /lib/Signature/Pingback.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Abstract = require('./Abstract'); 4 | var crypto = require('crypto'); 5 | 6 | exports.calculateSignature = function(params, secret, version) { 7 | var baseString = ''; 8 | var params = params; 9 | var sig; 10 | 11 | if (params.sig) { 12 | sig = params.sig; 13 | delete params.sig; 14 | }; 15 | 16 | version = parseInt(version); 17 | 18 | if (version !== 1) { 19 | baseString = Abstract.sortObject(params); 20 | } else{ 21 | baseString = "uid="+params.uid+"goodsid="+params.goodsid+"slength="+params.slength+"speriod="+params.speriod+"type="+params.type+"ref="+params.ref; 22 | }; 23 | baseString += secret; 24 | var algorithm = (version === 3) ? 'sha256' : 'md5'; 25 | 26 | var shasum = crypto.createHash(algorithm).update(baseString); 27 | params.sig = sig; 28 | return shasum.digest('hex'); 29 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "paymentwall", 3 | "version": "1.2.5", 4 | "author": "Paymentwall Team ", 5 | "description": "Paymentwall Node.js Library", 6 | "main": "./index", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/paymentwall/paymentwall-node.git" 10 | }, 11 | "scripts": { 12 | "test": "cucumber.js" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/paymentwall/paymentwall-node/issues" 16 | }, 17 | "keywords": [ 18 | "payments", 19 | "paymentwall", 20 | "digital goods", 21 | "credit card", 22 | "sms", 23 | "bank transfer", 24 | "prepaid", 25 | "ewallet", 26 | "monetization", 27 | "cucumber" 28 | ], 29 | "devDependencies": { 30 | "cucumber": "1.*", 31 | "chai": "*", 32 | "request": "*", 33 | "cheerio": "*" 34 | }, 35 | "license": "MIT", 36 | "homepage": "http://www.paymentwall.com" 37 | } 38 | -------------------------------------------------------------------------------- /features/step_definitions/pingback.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | module.exports = function() { 4 | 5 | this.Given(/^Pingback GET parameters "([^"]*)"$/, function (arg1, callback) { 6 | this.queryData = arg1; 7 | callback(); 8 | }); 9 | 10 | this.Given(/^Pingback IP address "([^"]*)"$/, function (arg1, callback) { 11 | this.ipAddress = arg1; 12 | callback(); 13 | }); 14 | 15 | this.When(/^Pingback is constructed$/, function (callback) { 16 | this.pingback = new this.paymentwall.Pingback(this.queryData, this.ipAddress); 17 | callback(); 18 | }); 19 | 20 | this.Then(/^Pingback validation result should be "([^"]*)"$/, function (arg1, callback) { 21 | this.pingback.validate().toString().should.equal(arg1); 22 | callback(); 23 | }); 24 | 25 | this.Then(/^Pingback method "([^"]*)" should return "([^"]*)"$/, function (arg1, arg2, callback) { 26 | r = this.pingback[arg1](); 27 | r.toString().should.equal(arg2); 28 | callback(); 29 | }); 30 | 31 | } -------------------------------------------------------------------------------- /lib/HttpAction.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var querystring = require('querystring'); 4 | var Response = require('./Response/Abstract'); 5 | var http = require('http'); 6 | var https = require('https'); 7 | 8 | exports.runAction = function( post_options, post_data, https_action, callback){ 9 | 10 | var abstractResponse = function(res){ 11 | res.setEncoding('utf8'); 12 | res.on('data', function (String_chunk) { 13 | var JSON_chunk = JSON.parse(String_chunk); 14 | var response = new Response(JSON_chunk, String_chunk); 15 | callback(response); 16 | }); 17 | }; 18 | if (https_action !== true) { 19 | post_options.port = 80; 20 | var post_req = http.request(post_options, function(res) { 21 | abstractResponse(res); 22 | }); 23 | } else { 24 | post_options.port = 443; 25 | var post_req = https.request(post_options, function(res) { 26 | abstractResponse(res); 27 | }); 28 | } 29 | post_req.write(post_data); 30 | post_req.end(); 31 | }; -------------------------------------------------------------------------------- /features/charge.feature: -------------------------------------------------------------------------------- 1 | Feature: Charge 2 | In order to let users make payments on my website 3 | As a developer 4 | I want to be able to perform a charge using Brick 5 | 6 | Scenario: Should be able to create a test charge 7 | Given Public key "t_33c1806e0daf60fc31f2167f0e4d59" 8 | And Private key "t_85c66c2d7461e8885805f92dfd171c" 9 | When test token is retrieved 10 | Then charge should be successful 11 | 12 | Scenario: Should be able to refund a test charge 13 | Given Public key "t_33c1806e0daf60fc31f2167f0e4d59" 14 | And Private key "t_85c66c2d7461e8885805f92dfd171c" 15 | And charge ID "9557984691424639727_test" 16 | Then charge should be refunded 17 | 18 | Scenario: Should not be able to create a test charge with wrong CVV code 19 | Given Public key "t_33c1806e0daf60fc31f2167f0e4d59" 20 | And Private key "t_85c66c2d7461e8885805f92dfd171c" 21 | And CVV code "333" 22 | When test token is retrieved 23 | Then I see this error message "The transaction was declined. Please use a different card or contact your bank" -------------------------------------------------------------------------------- /lib/Onetimetoken.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var ApiObject = require('./ApiObject'), 4 | Card = require('./Card'), 5 | HttpAction = require('./HttpAction'), 6 | util = require('util'), 7 | querystring = require('querystring'); 8 | 9 | function Onetimetoken(number,exp_month,exp_year,cvv){ 10 | this.card = new Card(number,exp_month,exp_year,cvv); 11 | } 12 | 13 | util.inherits(Onetimetoken, ApiObject); 14 | 15 | util._extend(Onetimetoken.prototype, { 16 | 17 | createOnetimetoken: function(callback){ 18 | //set the post data 19 | var post_data = { 20 | 'public_key': this.appKey, 21 | 'card[number]': this.card.number, 22 | 'card[exp_month]': this.card.exp_month, 23 | 'card[exp_year]': this.card.exp_year, 24 | 'card[cvv]': this.card.cvv 25 | }; 26 | post_data = querystring.stringify(post_data); 27 | var post_options = this.createRequest('onetimetoken'); 28 | HttpAction.runAction(post_options, post_data, true, function(data){ 29 | callback(data); 30 | }); 31 | } 32 | 33 | }); 34 | 35 | 36 | module.exports = Onetimetoken; 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2010-2015 Paymentwall, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /lib/Config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function Config (apiType, appKey, secretKey) { 4 | Config.prototype.apiType = apiType; 5 | Config.prototype.appKey = appKey; 6 | Config.prototype.secretKey = secretKey; 7 | }; 8 | 9 | Config.prototype.API_VC = 1; 10 | Config.prototype.API_GOODS = 2; 11 | Config.prototype.API_CART = 3; 12 | Config.prototype.SIGNATURE_VERSION_1 = 1; 13 | Config.prototype.SIGNATURE_VERSION_2 = 2; 14 | Config.prototype.SIGNATURE_VERSION_3 = 3; 15 | Config.prototype.DEFAULT_SIGNATURE_VERSION = 3; 16 | Config.prototype.VC_CONTROLLER = 'ps'; 17 | Config.prototype.GOODS_CONTROLLER = 'subscription'; 18 | Config.prototype.CART_CONTROLLER = 'cart'; 19 | Config.prototype.WIDGET_BASE_URL = 'https://api.paymentwall.com/api'; 20 | 21 | Config.prototype.BRICK_ONETIMETOKEN_TEST_BASE_URL = 'pwgateway.com'; 22 | Config.prototype.BRICK_ONETIMETOKEN_TEST_PATH = '/api/token'; 23 | Config.prototype.BRICK_BASE_URL = 'api.paymentwall.com'; 24 | Config.prototype.BRICK_ONETIMETOKEN_PATH = '/api/brick/token'; 25 | Config.prototype.BRICK_CHARGE_PATH = '/api/brick/charge'; 26 | Config.prototype.BRICK_SUBSCRIPTION_CHARGE_PATH = '/api/brick/subscription'; 27 | 28 | 29 | Config.prototype.VERSION = '2.0.0'; 30 | 31 | module.exports = Config; -------------------------------------------------------------------------------- /lib/Product.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Product.TYPE_SUBSCRIPTION = 'subscription'; 4 | Product.TYPE_FIXED = 'fixed'; 5 | 6 | Product.PERIOD_TYPE_DAY = 'day'; 7 | Product.PERIOD_TYPE_WEEK = 'week'; 8 | Product.PERIOD_TYPE_MONTH = 'month'; 9 | Product.PERIOD_TYPE_YEAR = 'year'; 10 | 11 | function Product(productId, amount, currencyCode, name, productType, periodLength, periodType, recurring, trialProduct) { 12 | this.productId = productId; 13 | this.amount = (amount || 0.00).toFixed(2); 14 | this.currencyCode = currencyCode || null; 15 | this.name = name || null; 16 | this.productType = productType || Product.TYPE_FIXED; 17 | this.periodLength = periodLength || 0; 18 | this.periodType = periodType || null; 19 | this.recurring = recurring || false; 20 | if (productType === Product.TYPE_SUBSCRIPTION && recurring && recurring !== 0) { 21 | this.trialProduct = trialProduct || null; 22 | } 23 | } 24 | 25 | Product.prototype = { 26 | getId: function() { 27 | return this.productId; 28 | }, 29 | 30 | getAmount: function() { 31 | return this.amount; 32 | }, 33 | 34 | getCurrencyCode: function() { 35 | return this.currencyCode; 36 | }, 37 | 38 | getName: function() { 39 | return this.name; 40 | }, 41 | 42 | getType: function() { 43 | return this.productType; 44 | }, 45 | 46 | getPeriodType: function() { 47 | return this.periodType; 48 | }, 49 | 50 | getPeriodLength: function() { 51 | return this.periodLength; 52 | }, 53 | 54 | isRecurring: function() { 55 | return this.recurring; 56 | }, 57 | 58 | getTrialProduct: function() { 59 | return this.trialProduct; 60 | } 61 | }; 62 | 63 | module.exports = Product; -------------------------------------------------------------------------------- /lib/ApiObject.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Base = require('./Base'), 4 | util = require('util'), 5 | querystring = require('querystring'); 6 | 7 | function ApiObject() { 8 | } 9 | 10 | util.inherits(ApiObject, Base); 11 | 12 | util._extend(ApiObject.prototype, { 13 | 14 | checkProjectEnv: function(){ 15 | var reg = /_+/; 16 | return reg.test(this.secretKey); 17 | }, 18 | 19 | createPostOptions: function(url, path, method){ 20 | // set the request options 21 | var post_options = { 22 | host: url, 23 | path: path, 24 | method: method, 25 | headers: { 26 | 'Content-Type': 'application/x-www-form-urlencoded', 27 | 'X-ApiKey': this.secretKey 28 | } 29 | }; 30 | return post_options; 31 | }, 32 | 33 | createRequest: function(type, additional_path){ 34 | var url; 35 | var path; 36 | var method; 37 | var additional_path = additional_path||''; 38 | if (!this.checkProjectEnv() && type === "onetimetoken") { 39 | method = "Post"; 40 | url = this.BRICK_ONETIMETOKEN_TEST_BASE_URL; 41 | path = this.BRICK_ONETIMETOKEN_TEST_PATH; 42 | } else { 43 | url = this.BRICK_BASE_URL; 44 | method = "Post"; 45 | if (type === "onetimetoken") { 46 | path = this.BRICK_ONETIMETOKEN_PATH; 47 | } else if (type === "charge"){ 48 | path = this.BRICK_CHARGE_PATH+additional_path; 49 | } else if (type === "subscription"){ 50 | path = this.BRICK_SUBSCRIPTION_CHARGE_PATH+additional_path; 51 | }; 52 | }; 53 | 54 | var post_options = this.createPostOptions(url, path, method); 55 | return post_options; 56 | }, 57 | 58 | }); 59 | 60 | module.exports = ApiObject; 61 | -------------------------------------------------------------------------------- /lib/Charge.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var HttpAction = require('./HttpAction'), 4 | ApiObject = require('./ApiObject'), 5 | util = require('util'), 6 | querystring = require('querystring'); 7 | 8 | function Charge(amount,currency,description,email,fingerprint,token,extra){ 9 | this.amount = amount||null; 10 | this.currency = currency||'USD'; 11 | this.description = description||null; 12 | this.email = email||null; 13 | this.fingerprint = fingerprint||null; 14 | this.token = token||null; 15 | this.extra = extra||null; 16 | } 17 | 18 | util.inherits(Charge, ApiObject); 19 | 20 | util._extend(Charge.prototype, { 21 | 22 | createCharge: function(callback){ 23 | //set the post data 24 | var post_data = { 25 | 'public_key': this.publickey, 26 | 'amount': this.amount, 27 | 'currency': this.currency, 28 | 'description': this.description, 29 | 'email': this.email, 30 | 'fingerprint': this.fingerprint, 31 | 'token': this.token 32 | }; 33 | 34 | post_data = this.objectMerge(post_data, this.extra); 35 | post_data = querystring.stringify(post_data); 36 | 37 | var post_options = this.createRequest('charge'); 38 | HttpAction.runAction(post_options, post_data, true, function(response){ 39 | callback(response); 40 | }); 41 | 42 | }, 43 | 44 | otherOperation: function(chargeid,type,callback){ 45 | var post_data = ''; 46 | var additional_path; 47 | switch(type){ 48 | case 'detail': additional_path = '/'+chargeid; 49 | break; 50 | case 'refund': additional_path = '/'+chargeid+'/refund'; 51 | break; 52 | case 'capture': additional_path = '/'+chargeid+'/capture'; 53 | break; 54 | case 'void': additional_path = '/'+chargeid+'/void'; 55 | break; 56 | default: console.log('Parameter error in charge.otherOperation'); 57 | break; 58 | } 59 | 60 | var post_options = this.createRequest('charge', additional_path); 61 | HttpAction.runAction(post_options, post_data, true, function(data){ 62 | callback(data); 63 | }); 64 | 65 | } 66 | }); 67 | 68 | module.exports = Charge; 69 | 70 | -------------------------------------------------------------------------------- /lib/Subscription.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var HttpAction = require('./HttpAction'), 4 | ApiObject = require('./ApiObject'), 5 | util = require('util'), 6 | querystring = require('querystring'); 7 | 8 | function Subscription(amount,currency,description,email,fingerprint,token,period,period_duration,trial_data,extra, plan){ 9 | this.amount = amount||null; 10 | this.currency = currency||'USD'; 11 | this.description = description||null; 12 | this.email = email||null; 13 | this.fingerprint = fingerprint||null; 14 | this.token = token||null; 15 | this.period = period||null; 16 | this.period_duration = period_duration||null; 17 | this.trial_data = trial_data||null; 18 | this.extra = extra||null; 19 | this.plan = plan; 20 | } 21 | 22 | util.inherits(Subscription, ApiObject); 23 | 24 | util._extend(Subscription.prototype, { 25 | 26 | createSubscription: function(callback){ 27 | //set the post data 28 | var post_data = { 29 | 'plan': this.plan, 30 | 'public_key': this.publickey, 31 | 'amount': this.amount, 32 | 'currency': this.currency, 33 | 'description': this.description, 34 | 'email': this.email, 35 | 'fingerprint': this.fingerprint, 36 | 'token': this.token, 37 | 'period': this.period, 38 | 'period_duration': this.period_duration 39 | }; 40 | 41 | post_data = this.objectMerge(post_data, this.trial_data); 42 | post_data = this.objectMerge(post_data, this.extra); 43 | post_data = querystring.stringify(post_data); 44 | 45 | var post_options = this.createRequest('subscription'); 46 | HttpAction.runAction(post_options, post_data, true, function(data){ 47 | callback(data); 48 | }); 49 | }, 50 | 51 | 52 | otherOperation: function(subscriptionid,type,callback){ 53 | var post_data = ''; 54 | var additional_path; 55 | switch(type){ 56 | case 'detail': additional_path = '/'+subscriptionid; 57 | break; 58 | case 'cancel': additional_path = '/'+subscriptionid+'/cancel'; 59 | break; 60 | default: console.log('Parameter error in subscription.otherOperation'); 61 | break; 62 | } 63 | 64 | var post_options = this.createRequest('subscription', additional_path); 65 | HttpAction.runAction(post_options, post_data, true, function(data){ 66 | callback(data); 67 | }); 68 | 69 | } 70 | }); 71 | 72 | module.exports = Subscription; 73 | -------------------------------------------------------------------------------- /features/step_definitions/charge.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function() { 3 | var token = ''; 4 | var chargeId = ''; 5 | var cvv = '432'; 6 | var card; 7 | 8 | this.When(/^test token is retrieved$/, function (callback) { 9 | var onetimetoken = new this.paymentwall.Onetimetoken( 10 | 4000000000000002, 11 | 01, 12 | 2017, 13 | cvv 14 | ); 15 | onetimetoken.createOnetimetoken(function(brick_response){ 16 | if(brick_response.isSuccessful()){ 17 | if(brick_response.isActivated()){ 18 | token = brick_response.getOnetimeToken(); //get onetimetoken 19 | card = brick_response.getCardInfo(); //get card information 20 | } 21 | } else{ 22 | error_code = brick_response.getErrorCode(); 23 | error_details = brick_response.getErrorDetails(); 24 | }; 25 | 26 | token.should.be.a('String'); 27 | card.should.be.a('Object'); 28 | callback(); 29 | }); 30 | }); 31 | 32 | this.Then(/^charge should be successful$/, function (callback) { 33 | var charge = new this.paymentwall.Charge( 34 | 0.5, 35 | 'USD', 36 | 'description', 37 | 'test@user.com', 38 | 123, 39 | token, 40 | {'custom[aad]':'aa'} 41 | ); 42 | 43 | charge.createCharge(function(brick_response){ 44 | brick_response.should.be.a('Object'); 45 | chargeId = brick_response.getChargeId(); 46 | callback(); 47 | }); 48 | }); 49 | 50 | this.Given(/^charge ID "([^"]*)"$/, function (arg1, callback) { 51 | callback(); 52 | }); 53 | 54 | this.Then(/^charge should be refunded$/, function (callback) { 55 | var charge = new this.paymentwall.Charge(); 56 | 57 | charge.otherOperation(chargeId,'refund',function(brick_response){ 58 | brick_response.should.be.a('Object'); 59 | callback(); 60 | }); 61 | }); 62 | 63 | this.Given(/^CVV code "([^"]*)"$/, function (arg1, callback) { 64 | cvv = arg1; 65 | callback(); 66 | }); 67 | 68 | this.Then(/^I see this error message "([^"]*)"$/, function (arg1, callback) { 69 | var charge = new this.paymentwall.Charge( 70 | 0.5, 71 | 'USD', 72 | 'description', 73 | 'test@user.com', 74 | 123, 75 | token, 76 | {'custom[aad]':'aa'} 77 | ); 78 | 79 | charge.createCharge(function(brick_response){ 80 | var error_message = brick_response.getErrorDetails(); 81 | error_message.should.be.equal(arg1); 82 | callback(); 83 | }); 84 | }); 85 | } -------------------------------------------------------------------------------- /features/step_definitions/widget.js: -------------------------------------------------------------------------------- 1 | var request = require('request'); 2 | var cheerio = require('cheerio'); 3 | 4 | module.exports = function() { 5 | var widgetSignatureVersion = ''; 6 | var productName = ''; 7 | var widget = '' 8 | var htmlBody = ''; 9 | var widgetCode = 'p10'; 10 | var languageCode = ''; 11 | 12 | this.Given(/^Widget signature version "([^"]*)"$/, function (arg1, callback) { 13 | widgetSignatureVersion = arg1; 14 | callback(); 15 | }); 16 | 17 | this.Given(/^Product name "([^"]*)"$/, function (arg1, callback) { 18 | productName = arg1; 19 | callback(); 20 | }); 21 | 22 | this.When(/^Widget is constructed$/, function (callback) { 23 | var extra = { 24 | 'email': 'user@hostname.com', 25 | 'sign_version': widgetSignatureVersion 26 | }; 27 | 28 | if (languageCode.length > 0) { 29 | extra['lang'] = languageCode; 30 | } 31 | 32 | widget = new this.paymentwall.Widget( 33 | 'test_user', 34 | widgetCode, 35 | [ 36 | new this.paymentwall.Product( 37 | 'product301', 38 | 9.99, 39 | 'USD', 40 | productName 41 | ) 42 | ], 43 | extra 44 | ); 45 | 46 | callback(); 47 | }); 48 | 49 | this.When(/^Widget HTML content is loaded$/, function (callback) { 50 | request(widget.getUrl(), function(err, resp, body) { 51 | if (err) 52 | throw err; 53 | htmlBody = body; 54 | 55 | callback(); 56 | }); 57 | }); 58 | 59 | this.Then(/^Widget HTML content should not contain "([^"]*)"$/, function (arg1, callback) { 60 | $ = cheerio.load(htmlBody); 61 | var bodyText = $('html > body').text(); 62 | 63 | if (bodyText.toLowerCase().indexOf(arg1.toLowerCase()) == -1) { 64 | callback(); 65 | } else { 66 | callback(new Error(arg1 + "found in HTML")); 67 | } 68 | }); 69 | 70 | this.Then(/^Widget HTML content should contain "([^"]*)"$/, function (arg1, callback) { 71 | $ = cheerio.load(htmlBody); 72 | var bodyText = $('html > body, h2').text(); 73 | if (bodyText.toLowerCase().indexOf(arg1.toLowerCase()) !== -1) { 74 | callback(); 75 | } else { 76 | callback(new Error(arg1 + " not found in HTML")); 77 | } 78 | }); 79 | 80 | this.Given(/^Widget code "([^"]*)"$/, function (arg1, callback) { 81 | widgetCode = arg1; 82 | callback(); 83 | }); 84 | 85 | this.Given(/^Language code "([^"]*)"$/, function (arg1, callback) { 86 | languageCode = arg1; 87 | callback(); 88 | }); 89 | 90 | this.Then(/^Widget URL should contain "([^"]*)"$/, function (arg1, callback) { 91 | if (widget.getUrl().search(arg1) !== -1) { 92 | callback(); 93 | } else { 94 | callback(new Error(arg1 + " not found in URL")); 95 | } 96 | }); 97 | } -------------------------------------------------------------------------------- /lib/Response/Abstract.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Success = require('./Success'); 3 | var Err = require('./Err'); 4 | 5 | function CallbackRes(JSON_chunk, String_chunk){ 6 | this.JSON_chunk = JSON_chunk; 7 | this.String_chunk = String_chunk; 8 | } 9 | 10 | CallbackRes.prototype = { 11 | 12 | isSuccessful: function(){ 13 | if (this.JSON_chunk.hasOwnProperty('type')) { 14 | if (this.JSON_chunk.type==='Error') { 15 | return false; 16 | } else { 17 | return true; 18 | }; 19 | } else if(this.JSON_chunk.hasOwnProperty('secure')){ 20 | return true; 21 | } else { 22 | console.log(this.getFullResponse()); 23 | return false; 24 | }; 25 | }, 26 | 27 | isCaptured: function(){ 28 | return Success.getParameter('capture',this.JSON_chunk); 29 | }, 30 | 31 | isRefunded: function(){ 32 | return Success.getParameter('refunded',this.JSON_chunk); 33 | }, 34 | 35 | isActivated: function(){ 36 | return Success.getParameter('active',this.JSON_chunk); 37 | }, 38 | 39 | isStarted: function(){ 40 | return Success.getParameter('started',this.JSON_chunk); 41 | }, 42 | 43 | isExpired: function(){ 44 | return Success.getParameter('expired',this.JSON_chunk); 45 | }, 46 | 47 | isUnderReview: function(){ 48 | return Success.getParameter('risk',this.JSON_chunk); 49 | }, 50 | 51 | getFullResponse: function(type){ 52 | if (type==="JSON") { 53 | return this.JSON_chunk; 54 | } else { 55 | return this.String_chunk; 56 | } 57 | }, 58 | 59 | get3DHtml: function(){ 60 | var secure = Success.getParameter('secure',this.JSON_chunk); 61 | return secure.formHTML; 62 | }, 63 | 64 | getChargeId: function(){ 65 | if (this.JSON_chunk.object==='charge') { 66 | return Success.getParameter('id',this.JSON_chunk); 67 | } else { 68 | var all_chargeid = Success.getParameter('charge',this.JSON_chunk); 69 | return all_chargeid[(all_chargeid.length-1)]; 70 | } 71 | 72 | }, 73 | 74 | getOnetimeToken: function(){ 75 | return Success.getParameter('token',this.JSON_chunk); 76 | }, 77 | 78 | getPermanentToken: function(){ 79 | if (this.JSON_chunk.hasOwnProperty('card')&&this.JSON_chunk.card!=null) { 80 | var card = Success.getParameter('token',this.JSON_chunk); 81 | return card.token; 82 | } else { 83 | return null; 84 | } 85 | }, 86 | 87 | getCardInfo: function(){ 88 | return Success.getParameter('card',this.JSON_chunk); 89 | }, 90 | 91 | getTrialInfo: function(){ 92 | return Success.getParameter('trial',this.JSON_chunk); 93 | }, 94 | 95 | getSubscriptionId: function(){ 96 | return Success.getParameter('id',this.JSON_chunk); 97 | }, 98 | 99 | getErrorCode: function(){ 100 | return Err.getParameter('code',this.JSON_chunk); 101 | }, 102 | 103 | getErrorDetails: function(){ 104 | return Err.getParameter('error',this.JSON_chunk); 105 | } 106 | 107 | }; 108 | 109 | module.exports = CallbackRes; -------------------------------------------------------------------------------- /lib/Widget.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Base = require('./Base'), 4 | Product = require('./Product'), 5 | util = require('util'), 6 | querystring = require('querystring'), 7 | Signature = require('./Signature/Widget'); 8 | 9 | function Widget(userId, widgetCode, products, extraParams) { 10 | this.errors = [] 11 | this.userId = userId || null; 12 | this.widgetCode = widgetCode || null; 13 | this.products = products || null; 14 | this.extraParams = extraParams || null; 15 | } 16 | 17 | util.inherits(Widget, Base); 18 | util._extend(Widget.prototype, { 19 | 20 | getDefaultWidgetSignature: function() { 21 | return this.getApiType() !== this.API_CART ? this.DEFAULT_SIGNATURE_VERSION : this.SIGNATURE_VERSION_2; 22 | }, 23 | 24 | getUrl: function() { 25 | var params = { 26 | 'key': this.getAppKey(), 27 | 'uid': this.userId, 28 | 'widget': this.widgetCode 29 | }; 30 | 31 | var productsNumber = this.products.length; 32 | 33 | if (this.getApiType() === this.API_GOODS) { 34 | if (this.products) { 35 | if (productsNumber === 1) { 36 | 37 | var product = this.products[0]; 38 | var postTrialProduct = null; 39 | if (product.getTrialProduct()) { 40 | postTrialProduct = product; 41 | product = product.getTrialProduct(); 42 | } 43 | 44 | params['amount'] = product.getAmount(); 45 | params['currencyCode'] = product.getCurrencyCode(); 46 | params['ag_name'] = product.getName(); 47 | params['ag_external_id'] = product.getId(); 48 | params['ag_type'] = product.getType(); 49 | 50 | if (product.getType() === Product.TYPE_SUBSCRIPTION) { 51 | 52 | params['ag_period_length'] = product.getPeriodLength(); 53 | params['ag_period_type'] = product.getPeriodType(); 54 | 55 | if (product.isRecurring()) { 56 | params['ag_recurring'] = product.isRecurring() ? 1 : 0; 57 | 58 | if (postTrialProduct !== null) { 59 | params['ag_trial'] = 1; 60 | params['ag_post_trial_external_id'] = postTrialProduct.getId(); 61 | params['ag_post_trial_period_length'] = postTrialProduct.getPeriodLength(); 62 | params['ag_post_trial_period_type'] = postTrialProduct.getPeriodType(); 63 | params['ag_post_trial_name'] = postTrialProduct.getName(); 64 | params['post_trial_amount'] = postTrialProduct.getAmount(); 65 | params['post_trial_currencyCode'] = postTrialProduct.getCurrencyCode(); 66 | } 67 | } 68 | } 69 | 70 | } else { 71 | //TODO: this.appendToErrors('Only 1 product is allowed in flexible widget call'); 72 | } 73 | 74 | } 75 | 76 | } else if (this.getApiType() === this.API_CART) { 77 | var index = 0; 78 | 79 | this.products.forEach(function(product) { 80 | params['external_ids[' + index + ']'] = product.getId(); 81 | 82 | if (product.amount > 0) { 83 | params['prices[' + index + ']'] = product.getAmount(); 84 | } 85 | if (product.currencyCode) { 86 | params['currencies[' + index + ']'] = product.getCurrencyCode(); 87 | } 88 | index++; 89 | }); 90 | } 91 | 92 | var signatureVersion; 93 | params['sign_version'] = signatureVersion = this.getDefaultWidgetSignature(); 94 | 95 | if (this.extraParams&&this.extraParams['sign_version']) { 96 | signatureVersion = params['sign_version'] = this.extraParams['sign_version']; 97 | } 98 | 99 | params = this.objectMerge(params, this.extraParams); 100 | 101 | params['sign'] = Signature.calculateSignature(params, this.getSecretKey(), signatureVersion); 102 | 103 | return this.WIDGET_BASE_URL + '/' + this.buildController(this.widgetCode) + '?' + querystring.stringify(params); 104 | }, 105 | 106 | getHtmlCode: function(attributes) { 107 | var attributes = attributes || {}; 108 | 109 | var defaultAttributes = { 110 | 'frameborder': '0', 111 | 'width': '750', 112 | 'height': '800' 113 | }; 114 | 115 | attributes = this.objectMerge(defaultAttributes, attributes); 116 | 117 | var attributesQuery = ''; 118 | for (var attr in attributes) { 119 | attributesQuery += ' ' + attr + '="' + attributes[attr] + '"'; 120 | } 121 | 122 | return ''; 123 | }, 124 | 125 | buildController: function(widget, flexibleCall) { 126 | var flexibleCall = flexibleCall || false; 127 | var pattern = /^w|s|mw/; 128 | 129 | if (this.getApiType() == this.API_VC) { 130 | if (!widget.match(pattern)) { 131 | return this.VC_CONTROLLER; 132 | } 133 | } else if (this.getApiType() === this.API_GOODS) { 134 | if (!flexibleCall && !widget.match(pattern)) { 135 | return this.GOODS_CONTROLLER; 136 | } 137 | } else { 138 | return this.CART_CONTROLLER; 139 | } 140 | 141 | return ''; 142 | }, 143 | 144 | }); 145 | 146 | module.exports = Widget; 147 | -------------------------------------------------------------------------------- /features/widget.feature: -------------------------------------------------------------------------------- 1 | Feature: Widget 2 | In order to let users make payments on my website 3 | As a developer 4 | I want to be able to generate HTML code of Paymentwall widgets 5 | 6 | Scenario: check Digital Goods widget signature v2 with correct secret key 7 | Given Public key "c22f895840bf2391f67a40da64bfed26" 8 | And Secret key "a7408723eaf4bfa2e3ac49b3cb695046" 9 | And API type "2" 10 | And Widget signature version "2" 11 | And Product name "Automatic Test Product Name" 12 | When Widget is constructed 13 | And Widget HTML content is loaded 14 | Then Widget HTML content should not contain "It looks like you're not authorized to view this content." 15 | And Widget HTML content should contain "Automatic Test Product Name" 16 | 17 | Scenario: check Digital Goods widget signature v2 with wrong secret key 18 | Given Public key "c22f895840bf2391f67a40da64bfed26" 19 | And Secret key "000" 20 | And API type "2" 21 | And Widget signature version "2" 22 | When Widget is constructed 23 | And Widget HTML content is loaded 24 | Then Widget HTML content should contain "It looks like you're not authorized to view this content." 25 | 26 | Scenario: check Digital Goods widget signature v1 with correct secret key 27 | Given Public key "c22f895840bf2391f67a40da64bfed26" 28 | And Secret key "a7408723eaf4bfa2e3ac49b3cb695046" 29 | And API type "2" 30 | And Widget signature version "1" 31 | When Widget is constructed 32 | And Widget HTML content is loaded 33 | Then Widget HTML content should not contain "It looks like you're not authorized to view this content." 34 | 35 | Scenario: check Digital Goods widget signature v1 with wrong secret key 36 | Given Public key "c22f895840bf2391f67a40da64bfed26" 37 | And Secret key "000" 38 | And API type "2" 39 | And Widget signature version "1" 40 | When Widget is constructed 41 | And Widget HTML content is loaded 42 | Then Widget HTML content should contain "It looks like you're not authorized to view this content." 43 | 44 | Scenario: check Digital Goods widget signature v3 with correct secret key 45 | Given Public key "c22f895840bf2391f67a40da64bfed26" 46 | And Secret key "a7408723eaf4bfa2e3ac49b3cb695046" 47 | And API type "2" 48 | And Widget signature version "3" 49 | When Widget is constructed 50 | And Widget HTML content is loaded 51 | Then Widget HTML content should not contain "It looks like you're not authorized to view this content." 52 | 53 | Scenario: check Digital Goods widget signature v3 with wrong secret key 54 | Given Public key "c22f895840bf2391f67a40da64bfed26" 55 | And Secret key "000" 56 | And API type "2" 57 | And Widget signature version "3" 58 | When Widget is constructed 59 | And Widget HTML content is loaded 60 | Then Widget HTML content should contain "It looks like you're not authorized to view this content." 61 | 62 | Scenario: check Digital Goods widget signature v3 with wrong secret key 63 | Given Public key "c22f895840bf2391f67a40da64bfed26" 64 | And Secret key "a7408723eaf4bfa2e3ac49b3cb695046" 65 | And API type "2" 66 | And Widget signature version "3" 67 | And Product name "Automatic Test Product Name" 68 | When Widget is constructed 69 | And Widget HTML content is loaded 70 | Then Widget HTML content should not contain "It looks like you're not authorized to view this content." 71 | And Widget HTML content should contain "Automatic Test Product Name" 72 | 73 | Scenario: check Virtual Currency offer widget signature v2 with correct secret key 74 | Given Public key "c10c60d07a2f4549a17902d683eb0b11" 75 | And Secret key "6274def95b105f1c92d341a8d3bc2e77" 76 | And API type "1" 77 | And Widget signature version "2" 78 | And Widget code "w1" 79 | And Language code "en" 80 | When Widget is constructed 81 | And Widget HTML content is loaded 82 | Then Widget URL should contain "/api/?" 83 | And Widget HTML content should not contain "It looks like you're not authorized to view this content." 84 | And Widget HTML content should contain "by completing offers below" 85 | 86 | Scenario: check Virtual Currency payment widget signature v2 with correct secret key 87 | Given Public key "c10c60d07a2f4549a17902d683eb0b11" 88 | And Secret key "6274def95b105f1c92d341a8d3bc2e77" 89 | And API type "1" 90 | And Widget signature version "2" 91 | And Widget code "p10" 92 | And Language code "en" 93 | When Widget is constructed 94 | And Widget HTML content is loaded 95 | Then Widget URL should contain "/api/ps?" 96 | And Widget HTML content should not contain "It looks like you're not authorized to view this content." 97 | And Widget HTML content should contain "Select payment method" 98 | 99 | Scenario: check Virtual Currency widget signature v2 with wrong secret key 100 | Given Public key "c10c60d07a2f4549a17902d683eb0b11" 101 | And Secret key "000" 102 | And API type "1" 103 | And Widget signature version "2" 104 | When Widget is constructed 105 | And Widget HTML content is loaded 106 | Then Widget HTML content should contain "It looks like you're not authorized to view this content." 107 | -------------------------------------------------------------------------------- /lib/Pingback.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Base = require('./Base'), 4 | Product = require('./Product'), 5 | util = require('util'), 6 | Signature = require('./Signature/Pingback'), 7 | querystring = require('querystring'); 8 | 9 | Pingback.PINGBACK_TYPE_REGULAR = 0; 10 | Pingback.PINGBACK_TYPE_GOODWILL = 1; 11 | Pingback.PINGBACK_TYPE_NEGATIVE = 2; 12 | 13 | function Pingback(parameters, ipAddress, pingbackForBrick) { 14 | this.errors = []; 15 | if(typeof(parameters) === "string"){ 16 | parameters = querystring.parse(parameters); 17 | } else if(parameters instanceof Object){ 18 | 19 | } else { 20 | console.log("Error: Please pass Object as the queryData in Paymentwall.Pingback(queryData, ip)"); 21 | } 22 | 23 | this.parameters = parameters; 24 | this.ipAddress = ipAddress; 25 | this.pingbackForBrick = pingbackForBrick||false; 26 | } 27 | 28 | util.inherits(Pingback, Base); 29 | 30 | util._extend(Pingback.prototype, { 31 | 32 | validate: function(skipIpWhitelistCheck) { 33 | var pingbackForBrick = this.pingbackForBrick; 34 | var skipIpWhitelistCheck = skipIpWhitelistCheck || false; 35 | var validated = false; 36 | 37 | if (this.isParametersValid()) { 38 | if (this.isIpAddressValid() || skipIpWhitelistCheck) { 39 | if (this.isSignatureValid()) { 40 | validated = true; 41 | } else { 42 | this.appendToErrors('Wrong signature'); 43 | } 44 | } else { 45 | this.appendToErrors('IP address is not whitelisted'); 46 | } 47 | } else { 48 | this.appendToErrors('Missing parameters'); 49 | } 50 | 51 | return validated; 52 | }, 53 | 54 | isSignatureValid: function() { 55 | var signatureParamsToSign = {}; 56 | var signatureParams = []; 57 | if (this.getApiType() === this.API_VC) { 58 | signatureParams = ['uid', 'currency', 'type', 'ref']; 59 | } else if (this.getApiType() === this.API_GOODS) { 60 | if (!this.pingbackForBrick) { 61 | signatureParams = ['uid', 'goodsid', 'slength', 'speriod', 'type', 'ref']; 62 | } else{ 63 | signatureParams = ['uid', 'slength', 'speriod', 'type', 'ref']; 64 | } 65 | } else { // API_CART 66 | signatureParams = ['uid', 'goodsid', 'type', 'ref']; 67 | this.parameters['sign_version'] = this.SIGNATURE_VERSION_2; 68 | } 69 | 70 | if (!this.parameters['sign_version'] || this.parameters['sign_version'] === this.SIGNATURE_VERSION_1) { 71 | 72 | var ref = this; 73 | 74 | signatureParams.forEach(function(field) { 75 | signatureParamsToSign[field] = (ref.parameters[field] !== undefined) ? ref.parameters[field] : null; 76 | }); 77 | this.parameters['sign_version'] = this.SIGNATURE_VERSION_1; 78 | 79 | } else { 80 | signatureParamsToSign = this.parameters; 81 | } 82 | 83 | var signatureCalculated = Signature.calculateSignature(signatureParamsToSign, this.getSecretKey(), this.parameters['sign_version']); 84 | var signaturePassed = (this.parameters['sig'] !== undefined) ? this.parameters['sig'] : null; 85 | return signaturePassed === signatureCalculated; 86 | }, 87 | 88 | isIpAddressValid: function() { 89 | var ipsWhitelist = [ 90 | '174.36.92.186', 91 | '174.36.96.66', 92 | '174.36.92.187', 93 | '174.36.92.192', 94 | '174.37.14.28' 95 | ]; 96 | 97 | var result = ipsWhitelist.indexOf(this.ipAddress) >= 0; 98 | if (!result) { 99 | const newIpRegexp = /^216\.127\.71\.(\d{1,3})$/; 100 | const match = this.ipAddress.match(newIpRegexp); 101 | if (!match || (match[1] < 0 || match[1] > 255)) { 102 | return false; 103 | } 104 | } 105 | 106 | return true; 107 | }, 108 | 109 | isParametersValid: function() { 110 | 111 | var errorsNumber = 0; 112 | var requiredParams = []; 113 | if (this.getApiType() === this.API_VC) { 114 | requiredParams = ['uid', 'currency', 'type', 'ref', 'sig']; 115 | } else if (this.getApiType() === this.API_GOODS) { 116 | if (!this.pingbackForBrick) { 117 | requiredParams = ['uid', 'goodsid', 'type', 'ref', 'sig']; 118 | } else{ 119 | requiredParams = ['uid', 'type', 'ref', 'sig']; 120 | } 121 | } else { // Cart API 122 | requiredParams = ['uid', 'goodsid', 'type', 'ref', 'sig']; 123 | } 124 | 125 | var ref = this; 126 | 127 | if (typeof ref.parameters !== 'object') { 128 | ref.parameters = querystring.parse(ref.parameters);} 129 | 130 | requiredParams.forEach(function(field) { 131 | if ((ref.parameters[field] === undefined) || ref.parameters[field] === '') { 132 | ref.appendToErrors('Parameter ' + field + ' is missing'); 133 | errorsNumber++; 134 | } 135 | }); 136 | 137 | return errorsNumber === 0; 138 | }, 139 | 140 | getParameter: function(param) { 141 | if (this.parameters[param] !== undefined) { 142 | return this.parameters[param]; 143 | } 144 | }, 145 | 146 | getType: function() { 147 | var type; 148 | var pingbackTypes = [ 149 | Pingback.PINGBACK_TYPE_REGULAR, 150 | Pingback.PINGBACK_TYPE_GOODWILL, 151 | Pingback.PINGBACK_TYPE_NEGATIVE 152 | ]; 153 | 154 | if (this.parameters['type']) { 155 | type = parseInt(this.parameters['type']); 156 | if (pingbackTypes.indexOf(type) >= 0) { 157 | return type; 158 | } 159 | } 160 | }, 161 | 162 | getUserId: function() { 163 | return this.getParameter('uid'); 164 | }, 165 | 166 | getVirtualCurrencyAmount: function() { 167 | return this.getParameter('currency'); 168 | }, 169 | 170 | getProductId: function() { 171 | return this.getParameter('goodsid'); 172 | }, 173 | 174 | getProductPeriodLength: function() { 175 | return this.getParameter('slength'); 176 | }, 177 | 178 | getProductPeriodType: function() { 179 | return this.getParameter('speriod'); 180 | }, 181 | 182 | getReferenceId: function() { 183 | return this.getParameter('ref'); 184 | }, 185 | 186 | getPingbackUniqueId: function() { 187 | return this.getReferenceId() + '_' + this.getType(); 188 | }, 189 | 190 | getProduct: function() { 191 | return new Product( 192 | this.getProductId(), 193 | 0, 194 | null, 195 | null, 196 | this.getProductPeriodLength() > 0 ? Product.TYPE_SUBSCRIPTION : Product.TYPE_FIXED, 197 | this.getProductPeriodLength(), 198 | this.getProductPeriodType() 199 | ); 200 | }, 201 | 202 | getProducts: function() { 203 | var result = []; 204 | var productIds = this.getParameter('goodsid'); 205 | 206 | if (productIds && productIds instanceof Array) { 207 | productIds.forEach(function(id) { 208 | result.push(new Product(id)); 209 | }); 210 | } 211 | return result; 212 | }, 213 | 214 | isDeliverable: function() { 215 | return (this.getType() === Pingback.PINGBACK_TYPE_REGULAR || this.getType() === Pingback.PINGBACK_TYPE_GOODWILL); 216 | }, 217 | 218 | isCancelable: function() { 219 | return this.getType() === Pingback.PINGBACK_TYPE_NEGATIVE; 220 | }, 221 | 222 | }); 223 | 224 | module.exports = Pingback; 225 | -------------------------------------------------------------------------------- /features/pingback.feature: -------------------------------------------------------------------------------- 1 | Feature: Pingback 2 | In order to account for Paymentwall payments on my website 3 | As a developer 4 | I want to be able to validate Paymentwall pingbacks 5 | 6 | 7 | Scenario: check Digital Goods pingback signature v2 with correct signature and correct IP 8 | Given Public key "c22f895840bf2391f67a40da64bfed26" 9 | And Secret key "a7408723eaf4bfa2e3ac49b3cb695046" 10 | And API type "2" 11 | And Pingback GET parameters "uid=test_user&goodsid=test_product&slength=5&speriod=month&type=0&ref=t123&is_test=1&sign_version=2&sig=754cff93c0eb859f6054bef143ad253c" 12 | And Pingback IP address "174.36.92.186" 13 | When Pingback is constructed 14 | Then Pingback validation result should be "true" 15 | And Pingback method "getUserId" should return "test_user" 16 | And Pingback method "getProductId" should return "test_product" 17 | And Pingback method "getProductPeriodLength" should return "5" 18 | And Pingback method "getProductPeriodType" should return "month" 19 | And Pingback method "getReferenceId" should return "t123" 20 | And Pingback method "isDeliverable" should return "true" 21 | And Pingback method "isCancelable" should return "false" 22 | 23 | 24 | Scenario: check Digital Goods pingback signature v2 with correct signature and wrong IP 25 | Given Public key "c22f895840bf2391f67a40da64bfed26" 26 | And Secret key "a7408723eaf4bfa2e3ac49b3cb695046" 27 | And API type "2" 28 | And Pingback GET parameters "uid=test_user&goodsid=test_product&slength=5&speriod=month&type=0&ref=t123&is_test=1&sign_version=2&sig=754cff93c0eb859f6054bef143ad253c" 29 | And Pingback IP address "1.2.3.4" 30 | When Pingback is constructed 31 | Then Pingback validation result should be "false" 32 | 33 | 34 | Scenario: check Digital Goods pingback signature v2 with correct signature and correct new(11/2018) IP 35 | Given Public key "c22f895840bf2391f67a40da64bfed26" 36 | And Secret key "a7408723eaf4bfa2e3ac49b3cb695046" 37 | And API type "2" 38 | And Pingback GET parameters "uid=test_user&goodsid=test_product&slength=5&speriod=month&type=0&ref=t123&is_test=1&sign_version=2&sig=754cff93c0eb859f6054bef143ad253c" 39 | And Pingback IP address "216.127.71.25" 40 | When Pingback is constructed 41 | Then Pingback validation result should be "true" 42 | 43 | 44 | Scenario: check Digital Goods pingback signature v2 with correct signature and wrong new(11/2018) IP 45 | Given Public key "c22f895840bf2391f67a40da64bfed26" 46 | And Secret key "a7408723eaf4bfa2e3ac49b3cb695046" 47 | And API type "2" 48 | And Pingback GET parameters "uid=test_user&goodsid=test_product&slength=5&speriod=month&type=0&ref=t123&is_test=1&sign_version=2&sig=754cff93c0eb859f6054bef143ad253c" 49 | And Pingback IP address "216.127.71.256" 50 | When Pingback is constructed 51 | Then Pingback validation result should be "false" 52 | 53 | 54 | Scenario: check Digital Goods pingback signature v2 with wrong signature and correct IP 55 | Given Public key "c22f895840bf2391f67a40da64bfed26" 56 | And Secret key "a7408723eaf4bfa2e3ac49b3cb695046" 57 | And API type "2" 58 | And Pingback GET parameters "uid=test_user&goodsid=test_product&slength=5&speriod=month&type=0&ref=t123&is_test=1&sign_version=2&sig=754cff93c0eb859f6054bef143ad253cfoo" 59 | And Pingback IP address "174.36.92.186" 60 | When Pingback is constructed 61 | Then Pingback validation result should be "false" 62 | 63 | 64 | Scenario: check Digital Goods negative pingback signature v3 with correct signature and correct IP 65 | Given Public key "c22f895840bf2391f67a40da64bfed26" 66 | And Secret key "a7408723eaf4bfa2e3ac49b3cb695046" 67 | And API type "2" 68 | And Pingback GET parameters "uid=test_user&goodsid=test_product&slength=-5&speriod=month&type=2&ref=t123&is_test=1&reason=9&sign_version=3&sig=2f67209c3e581313a70de9425efef49f35a74c0cdb7f93051b47e3c097011a71" 69 | And Pingback IP address "174.36.92.186" 70 | When Pingback is constructed 71 | Then Pingback validation result should be "true" 72 | And Pingback method "getProductPeriodLength" should return "-5" 73 | And Pingback method "getProductPeriodType" should return "month" 74 | And Pingback method "isDeliverable" should return "false" 75 | And Pingback method "isCancelable" should return "true" 76 | 77 | 78 | Scenario: check Digital Goods negative pingback signature v1 with correct signature 79 | Given Public key "c22f895840bf2391f67a40da64bfed26" 80 | And Secret key "a7408723eaf4bfa2e3ac49b3cb695046" 81 | And API type "2" 82 | And Pingback GET parameters "uid=test_user&goodsid=test_product&slength=-5&speriod=month&type=2&ref=t123&is_test=1&reason=9&sig=e7b2ff07bc0734c83ee14f32552b1c88" 83 | And Pingback IP address "174.36.92.186" 84 | When Pingback is constructed 85 | Then Pingback validation result should be "true" 86 | 87 | 88 | Scenario: check Digital Goods pingback signature v1 with correct signature 89 | Given Public key "c10c60d07a2f4549a17902d683eb0b11" 90 | And Secret key "6274def95b105f1c92d341a8d3bc2e77" 91 | And API type "1" 92 | And Pingback GET parameters "uid=test_user¤cy=1000&type=0&ref=t555&sig=43601b80b7e1928d7c6dadc2860b10bc" 93 | And Pingback IP address "174.36.92.186" 94 | When Pingback is constructed 95 | Then Pingback validation result should be "true" 96 | And Pingback method "getVirtualCurrencyAmount" should return "1000" 97 | 98 | 99 | Scenario: check Virtual Currency pingback signature v1 with wrong signature 100 | Given Public key "c10c60d07a2f4549a17902d683eb0b11" 101 | And Secret key "6274def95b105f1c92d341a8d3bc2e77" 102 | And API type "1" 103 | And Pingback GET parameters "uid=test_user¤cy=1000&type=0&ref=t555&is_test=1&sig=efaacb488ab8ee19321ad513b6908574foo" 104 | And Pingback IP address "174.36.92.186" 105 | When Pingback is constructed 106 | Then Pingback validation result should be "false" 107 | 108 | Scenario: check Digital Goods pingback signature v2 with correct signature 109 | Given Public key "c10c60d07a2f4549a17902d683eb0b11" 110 | And Secret key "6274def95b105f1c92d341a8d3bc2e77" 111 | And API type "1" 112 | And Pingback GET parameters "uid=test_user¤cy=1000&type=0&ref=t555&is_test=1&sign_version=2&sig=5057977f881bed13592bec928f062b31" 113 | And Pingback IP address "174.36.92.186" 114 | When Pingback is constructed 115 | Then Pingback validation result should be "true" 116 | And Pingback method "getVirtualCurrencyAmount" should return "1000" 117 | 118 | 119 | Scenario: check Virtual Currency pingback signature v2 with wrong signature 120 | Given Public key "c10c60d07a2f4549a17902d683eb0b11" 121 | And Secret key "6274def95b105f1c92d341a8d3bc2e77" 122 | And API type "1" 123 | And Pingback GET parameters "uid=test_user¤cy=1000&type=0&ref=t555&is_test=1&sign_version=2&sig=5057977f881bed13592bec928f062b31foo" 124 | And Pingback IP address "174.36.92.186" 125 | When Pingback is constructed 126 | Then Pingback validation result should be "false" 127 | 128 | 129 | Scenario: check Digital Goods pingback signature v3 with correct signature 130 | Given Public key "c10c60d07a2f4549a17902d683eb0b11" 131 | And Secret key "6274def95b105f1c92d341a8d3bc2e77" 132 | And API type "1" 133 | And Pingback GET parameters "uid=test_user¤cy=1000&type=0&ref=t555&is_test=1&sign_version=3&sig=a2932c360010e613166ae95ede5a3fa45bfcac10e1dd93715d21b00d684eb0fb" 134 | And Pingback IP address "174.36.92.186" 135 | When Pingback is constructed 136 | Then Pingback validation result should be "true" 137 | And Pingback method "getVirtualCurrencyAmount" should return "1000" 138 | 139 | 140 | Scenario: check Virtual Currency pingback signature v3 with wrong signature 141 | Given Public key "c10c60d07a2f4549a17902d683eb0b11" 142 | And Secret key "6274def95b105f1c92d341a8d3bc2e77" 143 | And API type "1" 144 | And Pingback GET parameters "uid=test_user¤cy=1000&type=0&ref=t555&is_test=1&sign_version=3&sig=a2932c360010e613166ae95ede5a3fa45bfcac10e1dd93715d21b00d684eb0fbfoo" 145 | And Pingback IP address "174.36.92.186" 146 | When Pingback is constructed 147 | Then Pingback validation result should be "false" 148 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # About Paymentwall 2 | [Paymentwall](http://paymentwall.com/?source=gh-node) is the leading digital payments platform for globally monetizing digital goods and services. Paymentwall assists game publishers, dating sites, rewards sites, SaaS companies and many other verticals to monetize their digital content and services. 3 | Merchants can plugin Paymentwall's API to accept payments from over 100 different methods including credit cards, debit cards, bank transfers, SMS/Mobile payments, prepaid cards, eWallets, landline payments and others. 4 | 5 | To sign up for a Paymentwall Merchant Account, [click here](http://paymentwall.com/signup/merchant?source=gh-node). 6 | 7 | # Paymentwall Node.js Library 8 | This library allows developers to use [Paymentwall APIs](http://paymentwall.com/en/documentation/API-Documentation/722?source=gh-node) (Virtual Currency, Digital Goods featuring recurring billing, and Virtual Cart). 9 | 10 | To use Paymentwall, all you need to do is to sign up for a Paymentwall Merchant Account so you can setup an Application designed for your site. 11 | To open your merchant account and set up an application, you can [sign up here](http://paymentwall.com/signup/merchant?source=gh-node). 12 | 13 | # Installation 14 | To install the library in your environment, simply run the following command: 15 | 16 | ``` 17 | npm install paymentwall 18 | ``` 19 | 20 | Then use a code sample below. 21 | 22 | # Code Samples 23 | 24 | ## Digital Goods API 25 | 26 | [Web API details](http://www.paymentwall.com/en/documentation/Digital-Goods-API/710#paymentwall_widget_call_flexible_widget_call) 27 | 28 | #### Initializing Paymentwall 29 | 30 | ```javascript 31 | var Paymentwall = require('paymentwall'); 32 | Paymentwall.Configure( 33 | Paymentwall.Base.API_GOODS, 34 | 'YOUR_APPLICATION_KEY', 35 | 'YOUR_SECRET_KEY' 36 | ); 37 | ``` 38 | 39 | #### Widget Call 40 | 41 | The widget is a payment page hosted by Paymentwall that embeds the entire payment flow: selecting the payment method, completing the billing details, and providing customer support via the Help section. You can redirect the users to this page or embed it via iframe. The sample code below renders an iframe with Paymentwall Widget. 42 | 43 | ```javascript 44 | var widget = new Paymentwall.Widget( 45 | 'user40012', // id of the end-user who's making the payment 46 | 'pw', // widget code, e.g. pw; can be picked in the Widgets section of your merchant account 47 | [ // product details for Flexible Widget Call. 48 | // Leave empty if product selection happens on Paymentwall's side 49 | new Paymentwall.Product( 50 | 'product301', // id of the product in your system 51 | 9.99, // price 52 | 'USD', // currency code 53 | 'Gold Membership', // product name 54 | // if it is a onetime charge product, you don't need to configure time-based part 55 | Paymentwall.Product.TYPE_SUBSCRIPTION, // this is a time-based product 56 | 1, // length of product period 57 | Paymentwall.Product.PERIOD_TYPE_MONTH, // type of product period 58 | true // this is a recurring product 59 | ) 60 | ], 61 | {'email': 'user@hostname.com'} // additional parameters. for full list check API docs 62 | ); 63 | console.log(widget.getHtmlCode()); 64 | ``` 65 | 66 | #### Pingback Processing 67 | 68 | The Pingback is a webhook notifying about a payment being made. Pingbacks are sent via HTTP/HTTPS to your servers. To process pingbacks use the following code: 69 | 70 | ```javascript 71 | var pingback = new Paymentwall.Pingback("query data in pingback request", "ip address of pingback"); 72 | if (pingback.validate()) { 73 | var productId = pingback.getProduct().getId(); 74 | if (pingback.isDeliverable()) { 75 | // deliver the product 76 | } else if (pingback.isCancelable()) { 77 | // withdraw the product 78 | } 79 | console.log('OK'); // Paymentwall expects the string OK in response, otherwise the pingback will be resent 80 | } else { 81 | console.log(pingback.getErrorSummary()); 82 | } 83 | ``` 84 | 85 | ## Virtual Currency API 86 | 87 | [Web API Details](https://www.paymentwall.com/en/documentation/Virtual-Currency-API/711) 88 | 89 | #### Initializing Paymentwall 90 | 91 | ```javascript 92 | var Paymentwall = require('paymentwall'); 93 | Paymentwall.Configure( 94 | Paymentwall.Base.API_VC, 95 | 'YOUR_APPLICATION_KEY', 96 | 'YOUR_SECRET_KEY' 97 | ); 98 | ``` 99 | 100 | #### Widget Call 101 | 102 | ```javascript 103 | var widget = new Paymentwall.Widget( 104 | 'user40012', 105 | 'p10', 106 | [], 107 | {'email': 'user@hostname.com'} 108 | ); 109 | console.log(widget.getHtmlCode()); 110 | ``` 111 | 112 | #### Pingback Processing 113 | 114 | ```javascript 115 | var pingback = new Paymentwall.Pingback("query data in pingback request", "ip address of pingback"); 116 | if (pingback.validate()) { 117 | var virtualCurrency = pingback.getVirtualCurrencyAmount(); 118 | if (pingback.isDeliverable()) { 119 | // deliver the virtual currency 120 | } else if (pingback.isCancelable()) { 121 | // withdraw the virtual currency 122 | } 123 | console.log('OK'); // Paymentwall expects the string OK in response, otherwise the pingback will be resent 124 | } else { 125 | console.log(pingback.getErrorSummary()); 126 | } 127 | ``` 128 | 129 | ## Cart API 130 | 131 | [Web API Details](https://www.paymentwall.com/en/documentation/Shopping-Cart-API/1098) 132 | 133 | #### Initializing Paymentwall 134 | 135 | ```javascript 136 | var Paymentwall = require('paymentwall'); 137 | Paymentwall.Configure( 138 | Paymentwall.Base.API_CART, 139 | 'YOUR_APPLICATION_KEY', 140 | 'YOUR_SECRET_KEY' 141 | ); 142 | ``` 143 | 144 | #### Widget Call 145 | 146 | ```javascript 147 | var widget = new Paymentwall.Widget( 148 | 'user40012', 149 | 'p10', 150 | [ 151 | new Paymentwall.Product('product301', 3.33, 'EUR'), // first product in cart 152 | new Paymentwall.Product('product607', 7.77, 'EUR') // second product in cart 153 | ], 154 | {'email': 'user@hostname.com'} 155 | ); 156 | console.log(widget.getHtmlCode()); 157 | ``` 158 | 159 | #### Pingback Processing 160 | 161 | ```javascript 162 | var pingback = new Paymentwall.Pingback("query data in pingback request", "ip address of pingback"); 163 | if (pingback.validate()) { 164 | var productId = pingback.getProduct().getId(); 165 | if (pingback.isDeliverable()) { 166 | // deliver the product 167 | } else if (pingback.isCancelable()) { 168 | // withdraw the product 169 | } 170 | console.log('OK'); // Paymentwall expects the string OK in response, otherwise the pingback will be resent 171 | } else { 172 | console.log(pingback.getErrorSummary()); 173 | } 174 | ``` 175 | ## Brick API 176 | 177 | [Web API details](https://www.paymentwall.com/en/documentation/Brick/2968) 178 | 179 | #### Initializing Paymentwall 180 | 181 | ```javascript 182 | var Paymentwall = require('paymentwall'); 183 | Paymentwall.Configure( 184 | Paymentwall.Base.API_GOODS, 185 | 'YOUR_APPLICATION_KEY', 186 | 'YOUR_SECRET_KEY' 187 | ); 188 | ``` 189 | 190 | #### Create a one-time token 191 | 192 | ```javascript 193 | var onetimetoken = new Paymentwall.Onetimetoken( 194 | 4000000000000002, // Card number, digits only 195 | 01, // Expiration month, 2 digits from 01 to 12 196 | 2017, // Expiration year, 4 digits 197 | 222 // CVC/CVV, 3-4 digits 198 | ); 199 | 200 | onetimetoken.createOnetimetoken(function(response){ 201 | // response is a new Response Object Entity (defined in paymentwall/lib/Response/Abstract) 202 | if(response.isSuccessful()){ 203 | if(response.isActivated()){ 204 | token = response.getOnetimeToken(); //get onetimetoken 205 | card = response.getCardInfo(); //get card information 206 | } 207 | } else{ 208 | error_code = response.getErrorCode(); 209 | error_details = response.getErrorDetails(); 210 | }; 211 | console.log(response.getFullResponse()); 212 | }); 213 | ``` 214 | 215 | #### Charge 216 | 217 | ```javascript 218 | var charge = new Paymentwall.Charge( 219 | 0.5, //price 220 | 'USD', //currency code 221 | 'description', //description of the product 222 | 'useremail@example.com', // user's email which can be gotten by req.body.email 223 | 'fingerprint', // fingerprint which can be gotten by req.body.brick_fingerprint 224 | 'onetimetoken', //one-time token 225 | {'custom[User_prfile_API]':'Value'} //custom parameters 226 | ); 227 | 228 | charge.createCharge(function(brick_response){ 229 | // brick_response is a new Response Object Entity (defined in paymentwall/lib/Response/Abstract) 230 | if(brick_response.isSuccessful()){ 231 | if(brick_response.isCaptured()){ 232 | charge_id = brick_response.getChargeId(); //deliver goods to user 233 | } else if(brick_response.isUnderReview()){ 234 | charge_id = brick_response.getChargeId(); //under risk review 235 | } else if(brick_response.isUnder3DSecure()){ 236 | return_page = brick_response.get3DHtml(); //return 3D secure page 237 | }; 238 | } else{ 239 | error_code = brick_response.getErrorCode(); //handle error 240 | error_details = brick_response.getErrorDetails(); 241 | }; 242 | 243 | brick_response.getFullResponse(); // get full response content in String format 244 | brick_response.getFullResponse('JSON'); // get full response content in JSON format 245 | }); 246 | ``` 247 | 248 | #### Charge Details 249 | 250 | ```javascript 251 | //get the charge details through chargeid 252 | var charge = new Paymentwall.Charge(); 253 | charge.otherOperation(chargeid,'detail',function(brick_response){ 254 | // brick_response is a new Response Object Entity (defined in paymentwall/lib/Response/Abstract) 255 | brick_response.getFullResponse(); // get full response content in String format 256 | brick_response.getFullResponse('JSON'); // get full response content in JSON format 257 | }); 258 | ``` 259 | 260 | #### Charge-capture 261 | 262 | ```javascript 263 | //capture a charge through chargeid 264 | var charge = new Paymentwall.Charge(); 265 | charge.otherOperation(chargeid,'capture',function(brick_response){ 266 | // brick_response is a new Response Object Entity (defined in paymentwall/lib/Response/Abstract) 267 | brick_response.getFullResponse(); // get full response content in String format 268 | brick_response.getFullResponse('JSON'); // get full response content in JSON format 269 | }); 270 | ``` 271 | 272 | #### Charge-void 273 | 274 | ```javascript 275 | //void a charge through chargeid 276 | var charge = new Paymentwall.Charge(); 277 | charge.otherOperation(chargeid,'void',function(brick_response){ 278 | // response is a new Response Object Entity (defined in paymentwall/lib/Response/Abstract) 279 | brick_response.getFullResponse(); // get full response content in String format 280 | brick_response.getFullResponse('JSON'); // get full response content in JSON format 281 | }); 282 | ``` 283 | 284 | #### Charge-refund 285 | 286 | ```javascript 287 | //refund a charge through chargeid 288 | var charge = new Paymentwall.Charge(); 289 | charge.otherOperation(chargeid,'refund',function(brick_response){ 290 | // response is a new Response Object Entity (defined in paymentwall/lib/Response/Abstract) 291 | brick_response.getFullResponse(); // get full response content in String format 292 | brick_response.getFullResponse('JSON'); // get full response content in JSON format 293 | }); 294 | ``` 295 | 296 | #### Subscription 297 | 298 | ```javascript 299 | //create a subscription 300 | var subscription = new Paymentwall.Subscription( 301 | 0.5, //price 302 | 'USD', //currency code 303 | 'description', //description of the product 304 | 'useremail@example.com', // user's email which can be gotten by req.body.email 305 | 'fingerprint', // fingerprint which can be gotten by req.body.brick_fingerprint 306 | 'onetimetoken', //one-time token 307 | 'day', // day/week/month/year 308 | 3, // duration 309 | { 310 | // parameters for trial period 311 | 'trial[amount]':0.5, 312 | 'trial[currency]':'USD', 313 | 'trial[period]':'day', 314 | 'trial[period_duration]':3 315 | }, 316 | {'custom[User_prfile_API]':'Value'} //custom parameters, if there is a trail, plan is required 317 | ); 318 | 319 | subscription.createSubscription(function(brick_response){ 320 | // brick_response is a new Response Object Entity (defined in paymentwall/lib/Response/Abstract) 321 | if(brick_response.isSuccessful()){ 322 | if(brick_response.isActivated()&&brick_response.isStarted()){ 323 | subscription_id = getSubscriptionId(); 324 | charge_id = brick_response.getChargeId(); //deliver goods to user 325 | } else if(brick_response.isUnderReview()){ 326 | subscription_id = getSubscriptionId(); 327 | charge_id = brick_response.getChargeId(); //under risk review 328 | } else if(brick_response.isUnder3DSecure()){ 329 | return_page = brick_response.get3DHtml(); //return 3D secure page 330 | }; 331 | } else{ 332 | error_code = brick_response.getErrorCode(); //handle error 333 | error_details = brick_response.getErrorDetails(); 334 | }; 335 | 336 | brick_response.getFullResponse(); // get full response content in String format 337 | brick_response.getFullResponse('JSON'); // get full response content in JSON format 338 | }); 339 | ``` 340 | 341 | #### Subscription-details 342 | 343 | ```javascript 344 | //get the subscription details through subscriptionid 345 | var subscription = new Paymentwall.Subscription(); 346 | subscription.otherOperation(subscriptionid,'detail',function(response){ 347 | // response is a new Response Object Entity (defined in paymentwall/lib/Response/Abstract) 348 | response.getFullResponse(); // get full response content in String format 349 | response.getFullResponse('JSON'); // get full response content in JSON format 350 | }); 351 | ``` 352 | 353 | #### Subscription-cancel 354 | 355 | ```javascript 356 | //cancel a subscription through subscriptionid 357 | var subscription = new Paymentwall.Subscription(); 358 | subscription.otherOperation(subscriptionid,'cancel',function(response){ 359 | // response is a new Response Object Entity (defined in paymentwall/lib/Response/Abstract) 360 | response.getFullResponse(); // get full response content in String format 361 | response.getFullResponse('JSON'); // get full response content in JSON format 362 | }); 363 | ``` 364 | 365 | #### Pingback Processing 366 | 367 | The Pingback is a webhook notifying about a payment being made. Pingbacks are sent via HTTP/HTTPS to your servers. To process pingbacks use the following code: 368 | 369 | ```javascript 370 | var pingback = new Paymentwall.Pingback("query data in pingback request", "ip address of pingback"); 371 | if (pingback.validate()) { 372 | var productId = pingback.getProduct().getId(); 373 | if (pingback.isDeliverable()) { 374 | // deliver the product 375 | } else if (pingback.isCancelable()) { 376 | // withdraw the product 377 | } 378 | console.log('OK'); // Paymentwall expects the string OK in response, otherwise the pingback will be resent 379 | } else { 380 | console.log(pingback.getErrorSummary()); 381 | } 382 | ``` 383 | 384 | ## Signature API 385 | 386 | [Web API details](https://www.paymentwall.com/en/documentation/Signature-Calculation/2313) 387 | 388 | #### Widget Signature 389 | 390 | ```javascript 391 | var Paymentwall = require('paymentwall'); 392 | var widget_signature = Paymentwall.WidgetSignature.calculateSignature(parameters,secret_key, signature_version); 393 | ``` 394 | 395 | #### Pingback Signature 396 | 397 | ```javascript 398 | var Paymentwall = require('paymentwall'); 399 | var pingback_signature = Paymentwall.PingbackSignature.calculateSignature(parameters,secret_key, signature_version); 400 | ``` 401 | --------------------------------------------------------------------------------