2016-02-19 23:47:44 -05:00
import PromiseRouter from '../PromiseRouter' ;
2018-09-01 13:58:06 -04:00
var request = require ( 'request' ) ;
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
2018-09-01 13:58:06 -04:00
const IAP _SANDBOX _URL = 'https://sandbox.itunes.apple.com/verifyReceipt' ;
const IAP _PRODUCTION _URL = 'https://buy.itunes.apple.com/verifyReceipt' ;
2016-02-19 13:06:02 -05:00
const APP _STORE _ERRORS = {
2018-09-01 13:58:06 -04:00
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.' ,
} ;
2016-02-19 13:06:02 -05:00
function appStoreError ( status ) {
status = parseInt ( status ) ;
2018-09-01 13:58:06 -04:00
var errorString = APP _STORE _ERRORS [ status ] || 'unknown error.' ;
return { status : status , error : errorString } ;
2016-02-19 13:06:02 -05:00
}
function validateWithAppStore ( url , receipt ) {
return new Promise ( function ( fulfill , reject ) {
2018-09-01 13:58:06 -04:00
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 ) ;
2016-02-19 13:06:02 -05:00
}
2018-09-01 13:58:06 -04:00
) ;
2016-02-19 13:06:02 -05:00
} ) ;
}
function getFileForProductIdentifier ( productIdentifier , req ) {
2018-09-01 13:58:06 -04:00
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.'
) ;
}
2016-12-01 10:24:46 -08:00
2018-09-01 13:58:06 -04:00
var download = products [ 0 ] . download ;
return Promise . resolve ( { response : download } ) ;
} ) ;
2016-02-19 13:06:02 -05:00
}
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 ;
2016-12-01 10:24:46 -08:00
2018-09-01 13:58:06 -04:00
if ( ! receipt || ! productIdentifier ) {
2016-02-19 23:47:44 -05:00
// TODO: Error, malformed request
2018-09-01 13:58:06 -04:00
throw new Parse . Error (
Parse . Error . INVALID _JSON ,
'missing receipt or productIdentifier'
) ;
2016-02-19 23:47:44 -05:00
}
2016-12-01 10:24:46 -08:00
2016-02-19 23:47:44 -05:00
// Transform the object if there
// otherwise assume it's in Base64 already
2018-09-01 13:58:06 -04:00
if ( typeof receipt == 'object' ) {
if ( receipt [ '__type' ] == 'Bytes' ) {
2016-02-19 23:47:44 -05:00
receipt = receipt . base64 ;
}
}
2016-12-01 10:24:46 -08:00
2018-09-01 13:58:06 -04:00
if ( process . env . TESTING == '1' && req . body . bypassAppStoreValidation ) {
2016-02-19 23:47:44 -05:00
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 ) {
2018-09-01 13:58:06 -04:00
return Promise . resolve ( { response : appStoreError ( error . status ) } ) ;
2016-07-12 02:38:42 +03:00
}
2016-12-01 10:24:46 -08:00
2018-09-01 13:58:06 -04:00
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 ) ;
}
) ;
2016-11-24 15:47:41 -05:00
}
2016-07-12 02:38:42 +03:00
2018-09-01 13:58:06 -04:00
return errorCallback ( error ) ;
}
) ;
2016-02-19 13:06:02 -05:00
}
2016-12-01 10:24:46 -08:00
2016-02-19 23:47:44 -05:00
mountRoutes ( ) {
2018-09-01 13:58:06 -04:00
this . route ( 'POST' , '/validate_purchase' , this . handleRequest ) ;
2016-02-19 13:06:02 -05:00
}
}