2016-02-19 23:47:44 -05:00
import PromiseRouter from '../PromiseRouter' ;
2018-09-23 12:31:08 -04:00
const request = require ( '../request' ) ;
const 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 ) {
2018-09-23 12:31:08 -04:00
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 ;
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
}
}