2016-02-19 23:47:44 -05:00
import PromiseRouter from '../PromiseRouter' ;
2016-02-19 13:06:02 -05:00
var request = require ( "request" ) ;
2016-02-19 23:47:44 -05:00
var rest = require ( "../rest" ) ;
2016-11-24 15:47:41 -05:00
import Parse from 'parse/node' ;
2016-02-19 13:06:02 -05:00
2016-02-19 23:47:44 -05:00
// TODO move validation logic in IAPValidationController
2016-02-19 13:06:02 -05:00
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 new Promise ( function ( fulfill , reject ) {
request . post ( {
url : url ,
body : { "receipt-data" : receipt } ,
json : true ,
} , function ( err , res , body ) {
var status = body . status ;
if ( status == 0 ) {
// No need to pass anything, status is OK
return fulfill ( ) ;
}
// receipt is from test and should go to test
return reject ( body ) ;
} ) ;
} ) ;
}
function getFileForProductIdentifier ( productIdentifier , req ) {
2016-07-12 10:06:13 -04:00
return rest . find ( req . config , req . auth , '_Product' , { productIdentifier : productIdentifier } , undefined , req . info . clientSDK ) . then ( function ( result ) {
2016-02-19 13:06:02 -05:00
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 } ) ;
} ) ;
}
2016-02-19 23:47:44 -05:00
export class IAPValidationRouter extends PromiseRouter {
2016-11-24 15:47:41 -05:00
handleRequest ( req ) {
2016-02-19 23:47:44 -05:00
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 . NODE _ENV == "test" && req . body . bypassAppStoreValidation ) {
return getFileForProductIdentifier ( productIdentifier , req ) ;
2016-02-19 13:06:02 -05:00
}
2016-07-12 02:38:42 +03:00
function successCallback ( ) {
2016-11-24 15:47:41 -05:00
return getFileForProductIdentifier ( productIdentifier , req ) ;
}
2016-07-12 02:38:42 +03:00
function errorCallback ( error ) {
2016-11-24 15:47:41 -05:00
return Promise . resolve ( { response : appStoreError ( error . status ) } ) ;
2016-07-12 02:38:42 +03:00
}
2016-02-19 23:47:44 -05:00
return validateWithAppStore ( IAP _PRODUCTION _URL , receipt ) . then ( ( ) => {
2016-07-12 02:38:42 +03:00
return successCallback ( ) ;
2016-02-19 23:47:44 -05:00
} , ( error ) => {
2016-07-12 02:38:42 +03:00
if ( error . status == 21007 ) {
return validateWithAppStore ( IAP _SANDBOX _URL , receipt ) . then ( ( ) => {
2016-11-24 15:47:41 -05:00
return successCallback ( ) ;
} , ( error ) => {
return errorCallback ( error ) ;
}
2016-07-12 02:38:42 +03:00
) ;
}
return errorCallback ( error ) ;
2016-02-19 23:47:44 -05:00
} ) ;
2016-02-19 13:06:02 -05:00
}
2016-02-19 23:47:44 -05:00
mountRoutes ( ) {
this . route ( "POST" , "/validate_purchase" , this . handleRequest ) ;
2016-02-19 13:06:02 -05:00
}
}