Skip to content

Commit

Permalink
Add more common fields (#40)
Browse files Browse the repository at this point in the history
Added purchaseDate & expirationDate to all response objects.
  • Loading branch information
superandrew213 authored and ronkorving committed May 15, 2018
1 parent d3d5c1f commit 458c892
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 41 deletions.
19 changes: 13 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ var payment = {
packageName: 'my.app',
secret: 'password',
subscription: true, // optional, if google play subscription
keyObject: {}, // required, if google
userId: 'user id', // required, if amazon
devToken: 'developer id' // required, if roku
};
Expand All @@ -39,7 +40,7 @@ the next chapter for more information on the format.

## Supported platforms

### Amazon
### Amazon

**The payment object**

Expand Down Expand Up @@ -248,9 +249,15 @@ parsed receipt will be in the result object:
Regardless of the platform used, besides the platform-specific receipt, the following properties
will be included:

* transactionId, to uniquely identify this transaction.
* productId, which specifies what was purchased.
* platform, which is always the platform you passed.
| Property | Type | Description |
| --- | --- | --- |
| receipt | object | Data returned by platforms |
| platform | string | One of: 'apple', 'google', 'amazon', 'roku' |
| productId | string | Id of the product |
| transactionId | string | Id to uniquely identify transaction |
| purchaseDate | int | Date of purchase in millis |
| expirationDate | int | Date of expiration in millis |


## License

Expand All @@ -261,11 +268,11 @@ MIT
### Amazon References
**Code Inspiration**

* https://github.com/may215/amazon_iap
* https://github.com/may215/amazon_iap

**API Reference**

* https://developer.amazon.com/public/apis/earn/in-app-purchasing/docs-v2/verifying-receipts-in-iap
* https://developer.amazon.com/public/apis/earn/in-app-purchasing/docs-v2/verifying-receipts-in-iap

### Apple References
**Code Inspiration**
Expand Down
28 changes: 22 additions & 6 deletions lib/amazon/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,26 @@ var apiUrl = {
production: 'https://appstore-sdk.amazon.com/version/1.0/verifyReceiptId/developer/'
};

function parseResult(resultString) {
var result = JSON.parse(resultString);

var purchaseDate = result.purchaseDate ? parseInt(result.purchaseDate, 10) : null;
var expirationDate = null;
if (result.cancelDate) {
expirationDate = parseInt(result.cancelDate, 10);
} else if (result.renewalDate) {
expirationDate = parseInt(result.renewalDate, 10);
}

return {
receipt: result,
transactionId: result.receiptId,
productId: result.productId,
purchaseDate: purchaseDate,
expirationDate: expirationDate
};
}

exports.verifyPayment = function (payment, cb) {
try {
assert.equal(typeof payment.secret, 'string', 'Shared secret must be a string');
Expand All @@ -28,7 +48,7 @@ exports.verifyPayment = function (payment, cb) {
var resultObject = null;

try {
resultObject = JSON.parse(resultString);
resultObject = parseResult(resultString);
} catch (error) {
return cb(error);
}
Expand All @@ -53,10 +73,6 @@ exports.verifyPayment = function (payment, cb) {
return cb(new Error('Unknown operation exception'));
}

cb(null, {
receipt: resultObject,
transactionId: resultObject.receiptId,
productId: resultObject.productId
});
cb(null, resultObject);
});
};
30 changes: 25 additions & 5 deletions lib/apple/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ function getReceiptFieldValue(receipt, field) {
return null;
}

function parseTime(time) {
if (time) {
return parseInt(time, 10);
}
return null;
}

// RESULT DOCS https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html
function parseResult(result) {
result = JSON.parse(result);
Expand All @@ -39,7 +46,7 @@ function parseResult(result) {

var latestReceiptInfo = null;

if (status !== 0) {
if (status !== 0 && status !== 21006) {
var msg = responses[status] || 'Unknown status code: ' + status;

var error = new Error(msg);
Expand All @@ -50,20 +57,31 @@ function parseResult(result) {

var productId = getReceiptFieldValue(result.receipt, 'product_id');
var transactionId = getReceiptFieldValue(result.receipt, 'transaction_id');
var purchaseData = parseTime(getReceiptFieldValue(result.receipt, 'purchase_date_ms'));
var expirationDate = parseTime(
getReceiptFieldValue(result.receipt, 'expires_date_ms') ||
getReceiptFieldValue(result.receipt, 'expires_date')
);

if (result.hasOwnProperty('latest_receipt_info')) {
if (Array.isArray(result.latest_receipt_info) && result.latest_receipt_info.length > 0) {
latestReceiptInfo = result.latest_receipt_info.sort(function (a, b) {
return parseInt(a.transaction_id, 10) - parseInt(b.transaction_id, 10);
});

productId = latestReceiptInfo[latestReceiptInfo.length - 1].product_id;
transactionId = latestReceiptInfo[latestReceiptInfo.length - 1].transaction_id;
var lastReceipt = latestReceiptInfo[latestReceiptInfo.length - 1];
productId = lastReceipt.product_id;
transactionId = lastReceipt.transaction_id;
purchaseData = parseTime(lastReceipt.purchase_date_ms);
expirationDate = parseTime(lastReceipt.expires_date_ms || lastReceipt.expires_date);
} else if (result.latest_receipt_info && result.latest_receipt_info.transaction_id) {
latestReceiptInfo = [result.latest_receipt_info];

productId = result.latest_receipt_info.product_id;
transactionId = result.latest_receipt_info.transaction_id;
purchaseData = parseTime(result.latest_receipt_info.purchase_date_ms);
expirationDate = parseTime(
result.latest_receipt_info.expires_date_ms || result.latest_receipt_info.expires_date
);
}
}

Expand All @@ -72,7 +90,9 @@ function parseResult(result) {
latestReceiptInfo: latestReceiptInfo,
latestExpiredReceiptInfo: result.latest_expired_receipt_info,
productId: productId,
transactionId: transactionId
transactionId: transactionId,
purchaseData: purchaseData,
expirationDate: expirationDate
};
}

Expand Down
28 changes: 19 additions & 9 deletions lib/google/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ var jwt = require('./jwt');
var apiUrls = require('./urls');
var https = require('../https');

function parseResult(resultString, payment) {
var result = JSON.parse(resultString);
var purchaseTimeMillis = result.startTimeMillis || result.purchaseTimeMillis;
var purchaseDate = purchaseTimeMillis ? parseInt(purchaseTimeMillis, 10) : null;
var expirationDate = result.expiryTimeMillis ? parseInt(result.expiryTimeMillis, 10) : null;

return {
receipt: result,
transactionId: payment.receipt,
productId: payment.productId,
purchaseDate: purchaseDate,
expirationDate: expirationDate
};
}

exports.verifyPayment = function (payment, cb) {
var keyObject;
Expand Down Expand Up @@ -53,27 +67,23 @@ exports.verifyPayment = function (payment, cb) {
);
}

https.get(requestUrl, null, function (error, res, responseString) {
https.get(requestUrl, null, function (error, res, resultString) {
if (error) {
return cb(error);
}

if (res.statusCode !== 200) {
return cb(new Error('Received ' + res.statusCode + ' status code with body: ' + responseString));
return cb(new Error('Received ' + res.statusCode + ' status code with body: ' + resultString));
}

var responseObject;
var resultObject;
try {
responseObject = JSON.parse(responseString);
resultObject = parseResult(resultString, payment);
} catch (e) {
return cb(e);
}

return cb(null, {
receipt: responseObject,
transactionId: payment.receipt,
productId: payment.productId
});
return cb(null, resultObject);
});
});
};
39 changes: 24 additions & 15 deletions lib/roku/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,26 @@ var apiUrl = {
production: 'https://apipub.roku.com/listen/transaction-service.svc/validate-transaction'
};

function parseResult(resultString) {
var result = JSON.parse(resultString);

// parse non-standard date properties (i.e. /Date(1483242628000-0800)/)
// in order to extract value by milliseconds
result.originalPurchaseDate = new Date(parseInt(result.originalPurchaseDate.substr(6), 10)).getTime();
result.purchaseDate = new Date(parseInt(result.purchaseDate.substr(6), 10)).getTime();
result.expirationDate = result.expirationDate ?
new Date(parseInt(result.expirationDate.substr(6), 10)).getTime() :
null;

return {
receipt: result,
transactionId: result.transactionId,
productId: result.productId,
purchaseDate: result.purchaseDate,
expirationDate: result.expirationDate
};
}

exports.verifyPayment = function (payment, cb) {
try {
assert.equal(typeof payment.devToken, 'string', 'Developer ID must be a string');
Expand All @@ -33,26 +53,15 @@ exports.verifyPayment = function (payment, cb) {
var resultObject = null;

try {
resultObject = JSON.parse(resultString);
resultObject = parseResult(resultString);
} catch (error) {
return cb(error);
}

if (resultObject.errorMessage) {
return cb(new Error(resultObject.errorMessage));
if (resultObject.receipt.errorMessage) {
return cb(new Error(resultObject.receipt.errorMessage));
}

// parse non-standard date properties (i.e. /Date(1483242628000-0800)/)
// in order to extract value by milliseconds

resultObject.expirationDate = new Date(parseInt(resultObject.expirationDate.substr(6), 10)).getTime();
resultObject.originalPurchaseDate = new Date(parseInt(resultObject.originalPurchaseDate.substr(6), 10)).getTime();
resultObject.purchaseDate = new Date(parseInt(resultObject.purchaseDate.substr(6), 10)).getTime();

cb(null, {
receipt: resultObject,
transactionId: resultObject.transactionId,
productId: resultObject.productId
});
cb(null, resultObject);
});
};

0 comments on commit 458c892

Please sign in to comment.