UNPKG

in-app-purchase-iec-develop

Version:

In-App-Purchase validation and subscription management for iOS, Android, Amazon, and Windows

176 lines (160 loc) 6.21 kB
var constants = require('../constants'); var request = require('request'); var fs = require('fs'); var responseData = require('./responseData'); var verbose = require('./verbose'); var VER = '2.0'; var SECRET = '{developerSecret}'; var UID = '{userId}'; var PTOKEN = '{purchaseToken}'; var ERRORS = { VALIDATION: { 400: 'The transaction represented by this Purchase Token is no longer valid.', 404: 'Unknown operation exception.', 496: 'Invalid sharedSecret', 497: 'Invalid User ID', 498: 'Invalid Purchase Token', 499: 'The Purchase Token was created with credentials that have expired, use renew to generate a valid purchase token.', 500: 'There was an Internal Server Error' }, RENEW: { 400: 'Bad Request', 404: 'Unknown operation exception.', 496: 'Invalid sharedSecret', 497: 'Invalid User ID', 498: 'Invalid Purchase Token', 500: 'There is an Internal Server Error' } }; var VALIDATION_PATH = 'https://appstore-sdk.amazon.com/version/' + VER + '/verify/developer/' + SECRET + '/user/' + UID + '/purchaseToken/' + PTOKEN; var RENEW_PATH = 'https://appstore-sdk.amazon.com/version/' + VER + '/renew/developer/' + SECRET + '/user/' + UID + '/purchaseToken/' + PTOKEN; var S_VAL_PATH = VALIDATION_PATH; var S_R_PATH = RENEW_PATH; var NAME = '<Amazon>'; var config; module.exports.readConfig = function (configIn) { config = configIn; // Apply any default settings to Request. if ('requestDefaults' in configIn) { request = request.defaults(configIn.requestDefaults); } verbose.setup(config); }; module.exports.setup = function (cb) { if (!config || !config.secret) { return cb(); } fs.exists(config.secret, function (exists) { var secret = ''; if (!exists) { // use the string value literally secret = config.secret; VALIDATION_PATH = VALIDATION_PATH.replace(SECRET, config.secret); RENEW_PATH = RENEW_PATH.replace(SECRET, config.secret); verbose.log(NAME, 'Secret:', config.secret); return cb(); } // assume it as a file path fs.readFile(config.secret, 'UTF-8', function (error, val) { if (error) { return cb(error); } secret = val.replace(/(\r|\n)/g, ''); VALIDATION_PATH = VALIDATION_PATH.replace(SECRET, secret); RENEW_PATH = RENEW_PATH.replace(SECRET, secret); verbose.log(NAME, 'Secret:', secret); cb(); }); }); }; /* receipt: { userId: <user ID amazon in-app-purchase-server responds with> receiptId: <receipt ID from amazon> } */ module.exports.validatePurchase = function (dSecret, receipt, cb) { var rpath = RENEW_PATH; var path; // override secret with dSecret to allow dynamically fed secret to validate if (dSecret) { verbose.log(NAME, 'Use dynamically fed secret:', dSecret); rpath = S_R_PATH.replace(SECRET, dSecret); var vpath = S_VAL_PATH.replace(SECRET, dSecret); path = vpath.replace(UID, receipt.userId); } else { path = VALIDATION_PATH.replace(UID, receipt.userId); } path = path.replace(PTOKEN, receipt.receiptId); verbose.log(NAME, 'Validate:', path, receipt); send(path, ERRORS.VALIDATION, function (error, res) { if (error) { if (res === 499) { // must be renewed and re-tried var renew = rpath.replace(UID, receipt.userId); renew = renew.replace(PTOKEN, receipt.receiptId); verbose.log(NAME, 'Purchase must be renewed (' + res + '):', renew); send(renew, ERRORS.RENEW, function (error, renewed) { if (error) { var renewedErrorRes = { status: renewed, message: ERRORS.RENEW[renewed] || 'Unkown' }; verbose.log(NAME, 'Failed to renew purchase:', renewedErrorRes); return cb(error, renewedErrorRes); } var renewedReceipt = { receiptId: renewed.purchaseToken, userId: receipt.userId }; verbose.log(NAME, 'Purchase renewed:', renewedReceipt); module.exports.validatePurchase(renewedReceipt, cb); }); return; } var errorRes = { status: res, message: ERRORS.VALIDATION[res] || 'Unknown' }; verbose.log(NAME, 'Validation failed:', errorRes); return cb(error, errorRes); } verbose.log(NAME, 'Validation successful:', res); cb(null, res); }); }; module.exports.getPurchaseData = function (purchase, options) { if (!purchase || !purchase.purchaseToken) { return null; } var now = Date.now(); if (options && options.ignoreExpired && purchase.expirationDate <= now) { return []; } var obj = responseData.parse(purchase); obj.transactionId = purchase.purchaseToken; obj.productId = purchase.sku; obj.purchaseData = purchase.itemType; obj.quantity = 1; obj.purchaseDate = purchase.startDate || now; obj.expirationDate = purchase.endDate || 0; return [ obj ]; }; function send(path, errorMap, cb) { request.get(path, function (error, response, body) { var errorMsg = errorMap[response.statusCode]; if (errorMsg) { return cb(new Error(errorMsg + ': ' + path), response.statusCode); } if (error) { error.message += ': ' + path; return cb(error); } var res = JSON.parse(body); res.status = 0; res.service = constants.SERVICES.AMAZON; cb(null, res); }); }