From 0d78e8cc1d38045c19b39ee8c4e6330076903f74 Mon Sep 17 00:00:00 2001 From: Reino Muhl <10620585+StaberindeZA@users.noreply.github.com> Date: Mon, 28 Oct 2024 14:50:12 -0400 Subject: [PATCH] feat(iap): add new apple iap fields to firestore Because: - Apple App Store Server API added new fields to the transaction and renewal info which should be recorded in the SubPlat Firestore. This commit: - Adds fields currency, price and storefront from transactionInfo - Adds fields currency and renewalPrice from renwalInfo - Throw an error if apple notification payload contains no data Closes #FXA-10549 --- package.json | 1 + .../iap/apple-app-store/purchase-manager.ts | 5 ++++ packages/fxa-auth-server/package.json | 1 - .../decoded_transaction_info.json | 3 ++ .../apple-app-store/subscription-purchase.js | 6 ++++ packages/fxa-shared/package.json | 1 - .../apple-app-store/subscription-purchase.ts | 14 +++++++++ yarn.lock | 30 +++++++++++-------- 8 files changed, 47 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index f6fb340b90..40a5b57637 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,7 @@ "@type-cacheable/core": "^14.1.0", "@type-cacheable/ioredis-adapter": "^10.0.4", "agentkeepalive": "^4.5.0", + "app-store-server-api": "^0.15.3", "axios": "^1.7.5", "base64url": "^3.0.1", "bn.js": "^5.2.1", diff --git a/packages/fxa-auth-server/lib/payments/iap/apple-app-store/purchase-manager.ts b/packages/fxa-auth-server/lib/payments/iap/apple-app-store/purchase-manager.ts index 69a5c00ea4..60970e5b5c 100644 --- a/packages/fxa-auth-server/lib/payments/iap/apple-app-store/purchase-manager.ts +++ b/packages/fxa-auth-server/lib/payments/iap/apple-app-store/purchase-manager.ts @@ -133,6 +133,11 @@ export class PurchaseManager extends PurchaseManagerBase { originalTransactionId: string; }> { const decodedPayload = await decodeNotificationPayload(payload); + + if (!decodedPayload.data) { + throw new Error('Decoded payload contains no data'); + } + const { bundleId, originalTransactionId } = await decodeTransaction( decodedPayload.data.signedTransactionInfo ); diff --git a/packages/fxa-auth-server/package.json b/packages/fxa-auth-server/package.json index a81e76fc21..bbfa47aa5c 100644 --- a/packages/fxa-auth-server/package.json +++ b/packages/fxa-auth-server/package.json @@ -75,7 +75,6 @@ "@types/mjml": "^4.7.4", "agentkeepalive": "^4.5.0", "ajv": "^8.17.1", - "app-store-server-api": "^0.7.0", "aws-sdk": "^2.1691.0", "buf": "0.1.1", "commander": "2.18.0", diff --git a/packages/fxa-auth-server/test/local/payments/fixtures/apple-app-store/decoded_transaction_info.json b/packages/fxa-auth-server/test/local/payments/fixtures/apple-app-store/decoded_transaction_info.json index b4797a7b31..99353f30c5 100644 --- a/packages/fxa-auth-server/test/local/payments/fixtures/apple-app-store/decoded_transaction_info.json +++ b/packages/fxa-auth-server/test/local/payments/fixtures/apple-app-store/decoded_transaction_info.json @@ -1,4 +1,5 @@ { + "currency": "USD", "transactionId": "2000000000000000", "originalTransactionId": "1000000000000000", "webOrderLineItemId": "2000000000000000", @@ -6,11 +7,13 @@ "productId": "skydiving.with.foxkeh", "subscriptionGroupIdentifier": "22222222", "purchaseDate": 1649329745000, + "price": 999, "originalPurchaseDate": 1627306493000, "expiresDate": 1649330045000, "quantity": 1, "type": "Auto-Renewable Subscription", "inAppOwnershipType": "PURCHASED", "signedDate": 1649792142801, + "storefront": "USA", "environment": "Production" } diff --git a/packages/fxa-auth-server/test/local/payments/iap/apple-app-store/subscription-purchase.js b/packages/fxa-auth-server/test/local/payments/iap/apple-app-store/subscription-purchase.js index c0c83ab839..7e7b27a697 100644 --- a/packages/fxa-auth-server/test/local/payments/iap/apple-app-store/subscription-purchase.js +++ b/packages/fxa-auth-server/test/local/payments/iap/apple-app-store/subscription-purchase.js @@ -42,6 +42,9 @@ describe('SubscriptionPurchase', () => { const decodedTransactionInfo = deepCopy(transactionInfo); const decodedRenewalInfo = deepCopy(renewalInfo); const verifiedAt = Date.now(); + const currency = 'USD'; + const price = 999; + const storefront = 'USA'; describe('fromApiResponse', () => { const expected = { autoRenewStatus, @@ -60,6 +63,9 @@ describe('SubscriptionPurchase', () => { originalPurchaseDate, autoRenewProductId, purchaseDate, + currency, + price, + storefront, }; it('parses an active subscription correctly', () => { diff --git a/packages/fxa-shared/package.json b/packages/fxa-shared/package.json index 2ab8d178e7..34950e2d79 100644 --- a/packages/fxa-shared/package.json +++ b/packages/fxa-shared/package.json @@ -290,7 +290,6 @@ "@fluent/langneg": "^0.7.0", "accept-language-parser": "^1.5.0", "ajv": "^8.17.1", - "app-store-server-api": "^0.7.0", "aws-sdk": "^2.1691.0", "buf": "^0.1.1", "celebrate": "^15.0.1", diff --git a/packages/fxa-shared/payments/iap/apple-app-store/subscription-purchase.ts b/packages/fxa-shared/payments/iap/apple-app-store/subscription-purchase.ts index 72d80d3950..687a4218c8 100644 --- a/packages/fxa-shared/payments/iap/apple-app-store/subscription-purchase.ts +++ b/packages/fxa-shared/payments/iap/apple-app-store/subscription-purchase.ts @@ -109,6 +109,11 @@ export class AppStoreSubscriptionPurchase { private renewalOfferIdentifier?: string; private revocationDate?: number; private revocationReason?: number; + private currency!: string; + private price!: number; + private storefront!: string; + private renewalCurrency?: string; + private renewalPrice?: number; // Library-managed properties userId?: string; // hex string for FxA user id @@ -136,6 +141,9 @@ export class AppStoreSubscriptionPurchase { purchase.transactionId = transactionInfo.transactionId; purchase.type = transactionInfo.type; purchase.verifiedAt = verifiedAt; + purchase.currency = transactionInfo.currency; + purchase.price = transactionInfo.price; + purchase.storefront = transactionInfo.storefront; if (renewalInfo.expirationIntent) { purchase.expirationIntent = renewalInfo.expirationIntent; @@ -179,6 +187,12 @@ export class AppStoreSubscriptionPurchase { if (transactionInfo.hasOwnProperty('revocationReason')) { purchase.revocationReason = transactionInfo.revocationReason; } + if (renewalInfo.currency) { + purchase.renewalCurrency = renewalInfo.currency; + } + if (renewalInfo.renewalPrice) { + purchase.renewalPrice = renewalInfo.renewalPrice; + } return purchase; } diff --git a/yarn.lock b/yarn.lock index 024bf791f9..70be72d152 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28145,14 +28145,14 @@ __metadata: languageName: node linkType: hard -"app-store-server-api@npm:^0.7.0": - version: 0.7.0 - resolution: "app-store-server-api@npm:0.7.0" +"app-store-server-api@npm:^0.15.3": + version: 0.15.3 + resolution: "app-store-server-api@npm:0.15.3" dependencies: - jose: ^4.8.1 - node-fetch: ^2.6.6 - uuid: ^8.3.2 - checksum: 8d02759b0f94b7729ecf72544707d2702ec1b7ea7463591c00665db7be9aeaa8c8e7e30b56b2fab055ff9be2eec827c500dc7e217d5c57d0a1daec61dae01cfc + jose: ^4.15.7 + node-fetch: ^2.7.0 + uuid: ^9.0.1 + checksum: 201452c27f5d22c598d205dcc5573f07dfb6d3d6181a08567a6a29b9347a5a2eb780ba739a26222ef0959982fecac6d43f8496d4b397aee911091c472d4bf132 languageName: node linkType: hard @@ -41186,7 +41186,6 @@ fsevents@~2.1.1: "@types/webpack": 5.28.5 agentkeepalive: ^4.5.0 ajv: ^8.17.1 - app-store-server-api: ^0.7.0 async-retry: ^1.3.3 audit-filter: ^0.5.0 aws-sdk: ^2.1691.0 @@ -42021,7 +42020,6 @@ fsevents@~2.1.1: "@types/uuid": ^10.0.0 accept-language-parser: ^1.5.0 ajv: ^8.17.1 - app-store-server-api: ^0.7.0 audit-filter: ^0.5.0 aws-sdk: ^2.1691.0 buf: ^0.1.1 @@ -42166,6 +42164,7 @@ fsevents@~2.1.1: "@typescript-eslint/eslint-plugin": ^5.59.1 "@typescript-eslint/parser": ^7.1.1 agentkeepalive: ^4.5.0 + app-store-server-api: ^0.15.3 autoprefixer: ^10.4.14 axios: ^1.7.5 babel-jest: ^29.7.0 @@ -49518,7 +49517,7 @@ fsevents@~2.1.1: languageName: node linkType: hard -"jose@npm:^4.11.4, jose@npm:^4.8.1": +"jose@npm:^4.11.4": version: 4.14.4 resolution: "jose@npm:4.14.4" checksum: 2d820a91a8fd97c05d8bc8eedc373b944a0cd7f5fe41063086da233d0473c73fb523912a9f026ea870782bd221f4a515f441a2d3af4de48c6f2c76dac5082377 @@ -49532,6 +49531,13 @@ fsevents@~2.1.1: languageName: node linkType: hard +"jose@npm:^4.15.7": + version: 4.15.9 + resolution: "jose@npm:4.15.9" + checksum: 41abe1c99baa3cf8a78ebbf93da8f8e50e417b7a26754c4afa21865d87527b8ac2baf66de2c5f6accc3f7d7158658dae7364043677236ea1d07895b040097f15 + languageName: node + linkType: hard + "jose@npm:^5.1.3": version: 5.2.4 resolution: "jose@npm:5.2.4" @@ -54505,7 +54511,7 @@ fsevents@~2.1.1: languageName: node linkType: hard -"node-fetch@npm:^2.6.6, node-fetch@npm:^2.6.7": +"node-fetch@npm:^2.6.7": version: 2.6.7 resolution: "node-fetch@npm:2.6.7" dependencies: @@ -54519,7 +54525,7 @@ fsevents@~2.1.1: languageName: node linkType: hard -"node-fetch@npm:^2.6.9": +"node-fetch@npm:^2.6.9, node-fetch@npm:^2.7.0": version: 2.7.0 resolution: "node-fetch@npm:2.7.0" dependencies: