From f6750d74dc5fd5ce552022076845d28b754f0dcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20D=E2=80=99Aquino?= Date: Wed, 29 Jan 2025 17:34:22 -0800 Subject: [PATCH] Add information about whether user is a member for more than one year MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel D’Aquino --- src/app_store_receipt_verifier.js | 2 +- src/router_config.js | 2 +- src/user_management.js | 28 ++++++++++++++++++++++++++++ test/router_config.test.js | 9 ++++++--- 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/app_store_receipt_verifier.js b/src/app_store_receipt_verifier.js index eabfc0f..997a89b 100644 --- a/src/app_store_receipt_verifier.js +++ b/src/app_store_receipt_verifier.js @@ -21,7 +21,7 @@ const MOCK_TRANSACTION_HISTORY = [{ type: "iap", id: "1", start_date: current_time(), - end_date: current_time() + 60 * 60 * 24 * 30, + end_date: current_time() + 365 * 24 * 60 * 60, purchased_date: current_time(), duration: null }]; diff --git a/src/router_config.js b/src/router_config.js index 3cf1d9a..ffb2d0b 100644 --- a/src/router_config.js +++ b/src/router_config.js @@ -160,7 +160,7 @@ function config_router(app) { } const transaction_id = req.body.transaction_id - if (!transaction_id) { + if (!transaction_id && transaction_id != 0) { // Xcode environment sends 0 as a transaction id, so it is not "missing" in that case invalid_request(res, 'Missing transaction_id') return } diff --git a/src/user_management.js b/src/user_management.js index 1c95d89..b068f0f 100644 --- a/src/user_management.js +++ b/src/user_management.js @@ -152,11 +152,36 @@ function mark_iap_history_was_refreshed(api, pubkey) { return { account: account, request_error: null } } +// Helper function to calculate the total days of membership from a transaction history +// @param {Transaction[]} transactions - The transaction history +// @returns {number} - The total membership time in seconds +// +// Implementation note: Measuring in seconds prevents doing divisions, which speeds up the calculation +function total_active_membership_time(transactions) { + return transactions.reduce((acc, transaction) => { + if(transaction.type === "iap" && transaction.end_date !== null && transaction.start_date !== null) { + return acc + (transaction.end_date - transaction.start_date) + } + else if(transaction.type === "legacy" && transaction.end_date !== null && transaction.start_date !== null) { + return acc + (transaction.end_date - transaction.start_date) + } + else if(transaction.type === "ln" && transaction.duration !== null) { + return acc + transaction.duration + } + return acc + }, 0); +} + function get_account_info_payload(subscriber_number, account, authenticated = false) { if (!account) return null const account_active = (account.expiry && current_time() < account.expiry) ? true : false + // We consider one year to be 360 days, to be a bit lenient with users who might have a few days of downtime in their subscription, and make sure everyone who roughly got a year of service gets the benefit during the announcement. + const one_year_in_seconds = 360 * 24 * 60 * 60 + // Performance optimization: We only calculate the total membership time if the account is active + const member_for_more_than_one_year = account_active ? total_active_membership_time(account.transactions) > one_year_in_seconds : false + return { pubkey: account.pubkey, @@ -165,6 +190,9 @@ function get_account_info_payload(subscriber_number, account, authenticated = fa subscriber_number: subscriber_number, active: account_active, testflight_url: (authenticated && account_active) ? process.env.TESTFLIGHT_URL : null, + attributes: { + member_for_more_than_one_year: member_for_more_than_one_year, + } } } diff --git a/test/router_config.test.js b/test/router_config.test.js index d06548d..5331dc2 100644 --- a/test/router_config.test.js +++ b/test/router_config.test.js @@ -9,8 +9,8 @@ const { v4: uuidv4 } = require('uuid') test('config_router - Account management routes', async (t) => { const account_info = { pubkey: 'abc123', - created_at: Date.now() - 60 * 60 * 24 * 30 * 1000, // 30 days ago - expiry: Date.now() + 60 * 60 * 24 * 30 * 1000 // 30 days + created_at: current_time() - 60 * 60 * 24 * 30, // 30 days ago + expiry: current_time() + 60 * 60 * 24 * 30 // 30 days }; const pubkeys_to_user_ids = { 'abc123': 1 @@ -86,7 +86,10 @@ test('config_router - Account management routes', async (t) => { subscriber_number: 1, expiry: account_info.expiry, active: true, - testflight_url: null + testflight_url: null, + attributes: { + member_for_more_than_one_year: false + } }; t.same(res.body, expectedData, 'Response should match expected value'); t.end();