Skip to content

Commit 622ee85

Browse files
authored
feat: Add enableProductPurchaseLegacyApi option to disable legacy IAP validation (#10228)
1 parent 1551faa commit 622ee85

File tree

7 files changed

+70
-3
lines changed

7 files changed

+70
-3
lines changed

spec/Deprecator.spec.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,29 @@ describe('Deprecator', () => {
171171
}
172172
});
173173

174+
it('logs deprecation for enableProductPurchaseLegacyApi when set', async () => {
175+
const logSpy = spyOn(Deprecator, '_logOption').and.callFake(() => {});
176+
177+
await reconfigureServer({ enableProductPurchaseLegacyApi: true });
178+
expect(logSpy).toHaveBeenCalledWith(
179+
jasmine.objectContaining({
180+
optionKey: 'enableProductPurchaseLegacyApi',
181+
changeNewKey: '',
182+
})
183+
);
184+
});
185+
186+
it('does not log deprecation for enableProductPurchaseLegacyApi when not set', async () => {
187+
const logSpy = spyOn(Deprecator, '_logOption').and.callFake(() => {});
188+
189+
await reconfigureServer();
190+
expect(logSpy).not.toHaveBeenCalledWith(
191+
jasmine.objectContaining({
192+
optionKey: 'enableProductPurchaseLegacyApi',
193+
})
194+
);
195+
});
196+
174197
it('does not log deprecation for requestComplexity limits when explicitly set', async () => {
175198
const logSpy = spyOn(Deprecator, '_logOption').and.callFake(() => {});
176199

spec/PurchaseValidation.spec.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,31 @@ describe('test validate_receipt endpoint', () => {
186186
});
187187
});
188188

189+
it('should disable validate_purchase endpoint when enableProductPurchaseLegacyApi is false', async () => {
190+
await reconfigureServer({ enableProductPurchaseLegacyApi: false });
191+
const ParseServer = require('../lib/ParseServer').default;
192+
const routers = ParseServer.promiseRouter({
193+
appId: 'test',
194+
options: { enableProductPurchaseLegacyApi: false },
195+
});
196+
const hasValidatePurchase = routers.routes.some(
197+
r => r.path === '/validate_purchase' && r.method === 'POST'
198+
);
199+
expect(hasValidatePurchase).toBe(false);
200+
});
201+
202+
it('should enable validate_purchase endpoint by default', async () => {
203+
const ParseServer = require('../lib/ParseServer').default;
204+
const routers = ParseServer.promiseRouter({
205+
appId: 'test',
206+
options: {},
207+
});
208+
const hasValidatePurchase = routers.routes.some(
209+
r => r.path === '/validate_purchase' && r.method === 'POST'
210+
);
211+
expect(hasValidatePurchase).toBe(true);
212+
});
213+
189214
it('should not be able to remove a require key in a _Product', done => {
190215
const query = new Parse.Query('_Product');
191216
query

src/Deprecator/Deprecations.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,9 @@ module.exports = [
7171
changeNewDefault: '200',
7272
solution: "Set 'requestComplexity.graphQLFields' to a positive integer appropriate for your app to limit the number of GraphQL field selections, or to '-1' to disable.",
7373
},
74+
{
75+
optionKey: 'enableProductPurchaseLegacyApi',
76+
changeNewKey: '',
77+
solution: "The product purchase API is an undocumented, unmaintained legacy feature that may not function as expected and will be removed in a future major version. We strongly advise against using it. Set 'enableProductPurchaseLegacyApi' to 'false' to disable it, or remove the option to accept the future removal.",
78+
},
7479
];

src/Options/Definitions.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,12 @@ module.exports.ParseServerOptions = {
238238
action: parsers.booleanParser,
239239
default: false,
240240
},
241+
enableProductPurchaseLegacyApi: {
242+
env: 'PARSE_SERVER_ENABLE_PRODUCT_PURCHASE_LEGACY_API',
243+
help: 'Deprecated. Enables the legacy product purchase API including the `_Product` class and the `/validate_purchase` endpoint. This is an undocumented, unmaintained legacy feature inherited from the original Parse platform that may not function as expected. We strongly advise against using it. It will be removed in a future major version.',
244+
action: parsers.booleanParser,
245+
default: true,
246+
},
241247
enableSanitizedErrorResponse: {
242248
env: 'PARSE_SERVER_ENABLE_SANITIZED_ERROR_RESPONSE',
243249
help: 'If set to `true`, error details are removed from error messages in responses to client requests, and instead a generic error message is sent. Default is `true`.',

src/Options/docs.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Options/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,10 @@ export interface ParseServerOptions {
305305
/* Enables the default express error handler for all errors
306306
:DEFAULT: false */
307307
enableExpressErrorHandler: ?boolean;
308+
/* Deprecated. Enables the legacy product purchase API including the `_Product` class and the `/validate_purchase` endpoint. This is an undocumented, unmaintained legacy feature inherited from the original Parse platform that may not function as expected. We strongly advise against using it. It will be removed in a future major version.
309+
:ENV: PARSE_SERVER_ENABLE_PRODUCT_PURCHASE_LEGACY_API
310+
:DEFAULT: true */
311+
enableProductPurchaseLegacyApi: ?boolean;
308312
/* Sets the number of characters in generated object id's, default 10
309313
:DEFAULT: 10 */
310314
objectIdSize: ?number;

src/ParseServer.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ class ParseServer {
345345
}
346346
api.use(middlewares.handleParseSession);
347347
this.applyRequestContextMiddleware(api, options);
348-
const appRouter = ParseServer.promiseRouter({ appId });
348+
const appRouter = ParseServer.promiseRouter({ appId, options });
349349
api.use(appRouter.expressRouter());
350350

351351
api.use(middlewares.handleParseErrors);
@@ -378,7 +378,7 @@ class ParseServer {
378378
return api;
379379
}
380380

381-
static promiseRouter({ appId }) {
381+
static promiseRouter({ appId, options }) {
382382
const routers = [
383383
new ClassesRouter(),
384384
new UsersRouter(),
@@ -390,7 +390,6 @@ class ParseServer {
390390
new SchemasRouter(),
391391
new PushRouter(),
392392
new LogsRouter(),
393-
new IAPValidationRouter(),
394393
new FeaturesRouter(),
395394
new GlobalConfigRouter(),
396395
new GraphQLRouter(),
@@ -402,6 +401,10 @@ class ParseServer {
402401
new SecurityRouter(),
403402
];
404403

404+
if (options?.enableProductPurchaseLegacyApi !== false) {
405+
routers.push(new IAPValidationRouter());
406+
}
407+
405408
const routes = routers.reduce((memo, router) => {
406409
return memo.concat(router.routes);
407410
}, []);

0 commit comments

Comments
 (0)