From a7a84d38b05797069e9e6388d72c756d54df225e Mon Sep 17 00:00:00 2001 From: Justin Page Date: Wed, 25 Jan 2017 13:22:22 -0800 Subject: [PATCH 1/5] Use share secret for apple IAP When shared secret is provided, for an Apple Subscription, use that shared secret in accessing receipt information. Resolve: #5, #8, #21 --- README.md | 4 ++++ lib/apple/index.js | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/README.md b/README.md index b300446..3a3b1ee 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ var payment = { receipt: 'receipt data', // always required productId: 'abc', packageName: 'my.app', + secret: 'password', subscription: true // optional, if google play subscription }; @@ -52,6 +53,9 @@ receipt as returned by the iOS SDK (in which case it will be automatically base6 Both productId and packageName (bundle ID) are optional, but when provided will be tested against. If the receipt does not match the provided values, an error will be returned. +To verify auto-renewable subscriptions you need to provide `secret` field that +contains your In-App Purchase Shared Secret + **The response** The response passed back to your callback will also be Apple specific. The entire parsed receipt diff --git a/lib/apple/index.js b/lib/apple/index.js index c77b791..14660d9 100644 --- a/lib/apple/index.js +++ b/lib/apple/index.js @@ -89,6 +89,10 @@ exports.verifyPayment = function (payment, cb) { }); } + if (payment.secret !== undefined) { + assert.equal(typeof payment.secret, 'string', 'Shared secret must be a string'); + jsonData.password = payment.secret; + } function checkReceipt(error, result, environment) { if (error) { From d616e9cf093e86d344171aa823d9f59dd7d7ee6a Mon Sep 17 00:00:00 2001 From: Justin Page Date: Thu, 26 Jan 2017 15:13:51 -0800 Subject: [PATCH 2/5] Remove product id and package name comparison Remove product id and package name comparison to support receipt lookup without knowledge of the receipt's product id or package name. From production tests, you are able to lookup a receipt's info without this information. By providing null or empty values for both fields, we are able to extract receipt info from apple. To take this further, we could remove the hasOwnProperty check all together to no longer require this property. --- lib/apple/index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/apple/index.js b/lib/apple/index.js index 14660d9..4c520cb 100644 --- a/lib/apple/index.js +++ b/lib/apple/index.js @@ -102,13 +102,13 @@ exports.verifyPayment = function (payment, cb) { var receipt = result.receipt; /* jshint camelcase:false */ - if (payment.hasOwnProperty('productId') && payment.productId !== receipt.product_id) { - return cb(new Error('Wrong product ID: ' + payment.productId + ' (expected: ' + receipt.product_id + ')')); + if (payment.hasOwnProperty('productId')) { + return cb(new Error('product ID not found')); } /* jshint camelcase:true */ - if (payment.hasOwnProperty('packageName') && payment.packageName !== receipt.bid) { - return cb(new Error('Wrong bundle ID: ' + payment.packageName + ' (expected: ' + receipt.bid + ')')); + if (payment.hasOwnProperty('packageName')) { + return cb(new Error('Package name not found')); } result.environment = environment; From 00356127f367ecd4a28563bff1bcdcc28472217f Mon Sep 17 00:00:00 2001 From: Justin Page Date: Thu, 26 Jan 2017 15:32:32 -0800 Subject: [PATCH 3/5] Check if product id or pacakge name is provided Formatted variable after receipt conditions --- lib/apple/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/apple/index.js b/lib/apple/index.js index 4c520cb..5691d7b 100644 --- a/lib/apple/index.js +++ b/lib/apple/index.js @@ -102,16 +102,16 @@ exports.verifyPayment = function (payment, cb) { var receipt = result.receipt; /* jshint camelcase:false */ - if (payment.hasOwnProperty('productId')) { + if (!payment.hasOwnProperty('productId')) { return cb(new Error('product ID not found')); } /* jshint camelcase:true */ - if (payment.hasOwnProperty('packageName')) { + if (!payment.hasOwnProperty('packageName')) { return cb(new Error('Package name not found')); } - result.environment = environment; + result.environment = environment; return cb(null, result); } From 8542d798e5fa62565d8c5bd419003cf7928fbc99 Mon Sep 17 00:00:00 2001 From: Justin Page Date: Thu, 26 Jan 2017 15:57:26 -0800 Subject: [PATCH 4/5] Use secret when running binary for apple receipt Provide --secret='shared-secret' as an additional argument when validating apple receipts --- bin/verify-apple.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/verify-apple.js b/bin/verify-apple.js index c6d38a0..a539cab 100755 --- a/bin/verify-apple.js +++ b/bin/verify-apple.js @@ -1,9 +1,9 @@ #!/usr/bin/env node -var argv = require('minimist')(process.argv.slice(2), { string: ['productId', 'packageName', 'receipt'] }); +var argv = require('minimist')(process.argv.slice(2), { string: ['productId', 'packageName', 'receipt', 'secret'] }); if (argv.help) { - console.log('Usage: ./verfiy.js --productId=abc --packageName=my.app --receipt=\'receipt-data\''); + console.log('Usage: ./verfiy.js --productId=abc --packageName=my.app --receipt=\'receipt-data\' --secret=\'shared secret\''); process.exit(1); } From 0ce88ef4fed7d9b86bd4f0e0d4dd236d82077420 Mon Sep 17 00:00:00 2001 From: Justin Page Date: Wed, 1 Feb 2017 17:14:38 -0800 Subject: [PATCH 5/5] Added back the property check for checkReceipt After looking over the conditions again as well as the readme, it has come to our attention that the productId and packageName can be passive if not provided at all. --- lib/apple/index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/apple/index.js b/lib/apple/index.js index 5691d7b..c93fd6c 100644 --- a/lib/apple/index.js +++ b/lib/apple/index.js @@ -102,13 +102,13 @@ exports.verifyPayment = function (payment, cb) { var receipt = result.receipt; /* jshint camelcase:false */ - if (!payment.hasOwnProperty('productId')) { - return cb(new Error('product ID not found')); + if (payment.hasOwnProperty('productId') && payment.productId !== receipt.product_id) { + return cb(new Error('Wrong product ID: ' + payment.productId + ' (expected: ' + receipt.product_id + ')')); } /* jshint camelcase:true */ - if (!payment.hasOwnProperty('packageName')) { - return cb(new Error('Package name not found')); + if (payment.hasOwnProperty('packageName') && payment.packageName !== receipt.bid) { + return cb(new Error('Wrong bundle ID: ' + payment.packageName + ' (expected: ' + receipt.bid + ')')); } result.environment = environment;