129 lines
3.8 KiB
JavaScript
129 lines
3.8 KiB
JavaScript
import PromiseRouter from '../PromiseRouter';
|
|
const request = require('../request');
|
|
const rest = require('../rest');
|
|
import Parse from 'parse/node';
|
|
|
|
// TODO move validation logic in IAPValidationController
|
|
const IAP_SANDBOX_URL = 'https://sandbox.itunes.apple.com/verifyReceipt';
|
|
const IAP_PRODUCTION_URL = 'https://buy.itunes.apple.com/verifyReceipt';
|
|
|
|
const APP_STORE_ERRORS = {
|
|
21000: 'The App Store could not read the JSON object you provided.',
|
|
21002: 'The data in the receipt-data property was malformed or missing.',
|
|
21003: 'The receipt could not be authenticated.',
|
|
21004: 'The shared secret you provided does not match the shared secret on file for your account.',
|
|
21005: 'The receipt server is not currently available.',
|
|
21006: 'This receipt is valid but the subscription has expired.',
|
|
21007: 'This receipt is from the test environment, but it was sent to the production environment for verification. Send it to the test environment instead.',
|
|
21008: 'This receipt is from the production environment, but it was sent to the test environment for verification. Send it to the production environment instead.',
|
|
};
|
|
|
|
function appStoreError(status) {
|
|
status = parseInt(status);
|
|
var errorString = APP_STORE_ERRORS[status] || 'unknown error.';
|
|
return { status: status, error: errorString };
|
|
}
|
|
|
|
function validateWithAppStore(url, receipt) {
|
|
return request({
|
|
url: url,
|
|
method: 'POST',
|
|
body: { 'receipt-data': receipt },
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
}).then(httpResponse => {
|
|
const body = httpResponse.data;
|
|
if (body && body.status === 0) {
|
|
// No need to pass anything, status is OK
|
|
return;
|
|
}
|
|
// receipt is from test and should go to test
|
|
throw body;
|
|
});
|
|
}
|
|
|
|
function getFileForProductIdentifier(productIdentifier, req) {
|
|
return rest
|
|
.find(
|
|
req.config,
|
|
req.auth,
|
|
'_Product',
|
|
{ productIdentifier: productIdentifier },
|
|
undefined,
|
|
req.info.clientSDK
|
|
)
|
|
.then(function(result) {
|
|
const products = result.results;
|
|
if (!products || products.length != 1) {
|
|
// Error not found or too many
|
|
throw new Parse.Error(
|
|
Parse.Error.OBJECT_NOT_FOUND,
|
|
'Object not found.'
|
|
);
|
|
}
|
|
|
|
var download = products[0].download;
|
|
return Promise.resolve({ response: download });
|
|
});
|
|
}
|
|
|
|
export class IAPValidationRouter extends PromiseRouter {
|
|
handleRequest(req) {
|
|
let receipt = req.body.receipt;
|
|
const productIdentifier = req.body.productIdentifier;
|
|
|
|
if (!receipt || !productIdentifier) {
|
|
// TODO: Error, malformed request
|
|
throw new Parse.Error(
|
|
Parse.Error.INVALID_JSON,
|
|
'missing receipt or productIdentifier'
|
|
);
|
|
}
|
|
|
|
// Transform the object if there
|
|
// otherwise assume it's in Base64 already
|
|
if (typeof receipt == 'object') {
|
|
if (receipt['__type'] == 'Bytes') {
|
|
receipt = receipt.base64;
|
|
}
|
|
}
|
|
|
|
if (process.env.TESTING == '1' && req.body.bypassAppStoreValidation) {
|
|
return getFileForProductIdentifier(productIdentifier, req);
|
|
}
|
|
|
|
function successCallback() {
|
|
return getFileForProductIdentifier(productIdentifier, req);
|
|
}
|
|
|
|
function errorCallback(error) {
|
|
return Promise.resolve({ response: appStoreError(error.status) });
|
|
}
|
|
|
|
return validateWithAppStore(IAP_PRODUCTION_URL, receipt).then(
|
|
() => {
|
|
return successCallback();
|
|
},
|
|
error => {
|
|
if (error.status == 21007) {
|
|
return validateWithAppStore(IAP_SANDBOX_URL, receipt).then(
|
|
() => {
|
|
return successCallback();
|
|
},
|
|
error => {
|
|
return errorCallback(error);
|
|
}
|
|
);
|
|
}
|
|
|
|
return errorCallback(error);
|
|
}
|
|
);
|
|
}
|
|
|
|
mountRoutes() {
|
|
this.route('POST', '/validate_purchase', this.handleRequest);
|
|
}
|
|
}
|