Skip to content

Commit

Permalink
Fix IAP receipt validation crash on receipt with invalid transaction ID
Browse files Browse the repository at this point in the history
During testing I noticed that sending receipt payloads that have a valid
format but invalid transaction ID would crash the server. This commit
fixes that and adds test coverage for this edge case.

Testing:
- All tests passing, both new and old
- Ran an iOS simulator containing an IAP signed locally by Xcode (the one that originally caused the crash) and verified the crash no longer occurs

Signed-off-by: Daniel D’Aquino <[email protected]>
  • Loading branch information
danieldaquino committed Mar 2, 2024
1 parent 9558b19 commit 680031a
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 8 deletions.
13 changes: 9 additions & 4 deletions src/app_store_receipt_verifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,15 @@ async function fetchTransactionHistory(client, transactionId) {
let transactions = [];
do {
const revisionToken = response !== null && response.revision !== null ? response.revision : null;
response = await client.getTransactionHistory(transactionId, revisionToken, transactionHistoryRequest);
if (response.signedTransactions) {
transactions = transactions.concat(response.signedTransactions);
continue;
try {
response = await client.getTransactionHistory(transactionId, revisionToken, transactionHistoryRequest);
if (response.signedTransactions) {
transactions = transactions.concat(response.signedTransactions);
continue;
}
} catch (error) {
debug("Error fetching IAP transaction history due to an error: %o", error);
break;
}
} while (response.hasMore);
return transactions;
Expand Down
13 changes: 10 additions & 3 deletions test/controllers/mock_iap_controller.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 30 additions & 1 deletion test/iap_flow.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
const test = require('tap').test;
const { PurpleTestController } = require('./controllers/purple_test_controller.js');
const { PURPLE_ONE_MONTH } = require('../src/invoicing.js');
const { MOCK_ACCOUNT_UUIDS, MOCK_IAP_DATES } = require('./controllers/mock_iap_controller.js');
const { MOCK_ACCOUNT_UUIDS, MOCK_IAP_DATES, MOCK_RECEIPT_DATA } = require('./controllers/mock_iap_controller.js');

test('IAP Flow — Expected flow', async (t) => {
// Initialize the PurpleTestController
Expand Down Expand Up @@ -70,3 +70,32 @@ test('IAP Flow — Invalid receipt should not be authorized or crash the server'

t.end();
});

test('IAP Flow — Invalid receipt should not be authorized or crash the server', async (t) => {
// Initialize the PurpleTestController
const purple_api_controller = await PurpleTestController.new(t);

const user_uuid = MOCK_ACCOUNT_UUIDS[1]

// Instantiate a new client
const user_pubkey_1 = purple_api_controller.new_client();

// Try to get the account info
const response = await purple_api_controller.clients[user_pubkey_1].get_account();
t.same(response.statusCode, 404);

// Simulate IAP purchase on the iOS side

purple_api_controller.set_account_uuid(user_pubkey_1, user_uuid); // Associate the pubkey with the user_uuid on the server
const receipt_base64 = MOCK_RECEIPT_DATA[user_uuid]; // Receipt with valid format but invalid transaction ID

// Send the receipt to the server to activate the account
const iap_response = await purple_api_controller.clients[user_pubkey_1].send_iap_receipt(user_uuid, receipt_base64);
t.same(iap_response.statusCode, 401);

// Read the account info now
const account_info_response = await purple_api_controller.clients[user_pubkey_1].get_account();
t.same(account_info_response.statusCode, 404);

t.end();
});

0 comments on commit 680031a

Please sign in to comment.