typescript/src/update-subs/apple.ts (68 lines of code) (raw):

import 'source-map-support/register'; import type { SQSEvent, SQSRecord } from 'aws-lambda'; import { Subscription } from '../models/subscription'; import type { AppleSubscriptionReference } from '../models/subscriptionReference'; import type { AppleValidationResponse } from '../services/appleValidateReceipts'; import { validateReceipt } from '../services/appleValidateReceipts'; import { appleBundleToPlatform } from '../services/appToPlatform'; import { PRODUCT_BILLING_PERIOD } from '../services/productBillingPeriod'; import { dateToSecondTimestamp, thirtyMonths } from '../utils/dates'; import { parseAndStoreSubscriptionUpdate } from './updatesub'; import { transactionIdToAppleStoreKitSubscriptionDataDerivationForExtra } from '../services/api-storekit'; export async function toAppleSubscription_async( response: AppleValidationResponse, ): Promise<Subscription> { const latestReceiptInfo = response.latestReceiptInfo; let autoRenewStatus = false; if (response.latestReceiptInfo.autoRenewStatus) { autoRenewStatus = true; } let cancellationDate: string | undefined; if (latestReceiptInfo.cancellationDate) { cancellationDate = latestReceiptInfo.cancellationDate.toISOString(); } const billingPeriod = PRODUCT_BILLING_PERIOD[latestReceiptInfo.productId]; if (billingPeriod === undefined) { console.warn( `Unable to get the billing period, unknown product ID ${latestReceiptInfo.productId}`, ); } var extra = ''; // Defining the two variables we need to call for the extra data const transaction_id: string = response.latestReceiptInfo.originalTransactionId; const appBundleId: string | undefined = response.latestReceiptInfo.bundleId; if (appBundleId !== undefined) { const extra_object = await transactionIdToAppleStoreKitSubscriptionDataDerivationForExtra(appBundleId, transaction_id); extra = JSON.stringify(extra_object); } const subscription = new Subscription( latestReceiptInfo.originalTransactionId, // subscriptionId latestReceiptInfo.originalPurchaseDate.toISOString(), // startTimestamp latestReceiptInfo.expiresDate.toISOString(), // endTimestamp cancellationDate, // cancellationTimestamp autoRenewStatus, // autoRenewing latestReceiptInfo.productId, // productId appleBundleToPlatform(response.latestReceiptInfo.bundleId)?.toString(), // platform latestReceiptInfo.trialPeriod || latestReceiptInfo.inIntroOfferPeriod, // freeTrial billingPeriod, // billingPeriod null, // googlePayload response.latestReceipt, // receipt response.originalResponse, // applePayload dateToSecondTimestamp(thirtyMonths(latestReceiptInfo.expiresDate)), // ttl extra, // extra ); return Promise.resolve(subscription); } function sqsRecordToAppleSubscription( record: SQSRecord, ): Promise<Subscription[]> { const subRef = JSON.parse(record.body) as AppleSubscriptionReference; // sandboxRetry is set to false such that in production we don't store any sandbox receipt that would have snuck all the way here // In CODE or locally the default endpoint will be sanbox therefore no retry is necessary return validateReceipt(subRef.receipt, { sandboxRetry: false }).then(async (subs) => Promise.all(subs.map(toAppleSubscription_async)), ); // `subs` here is a AppleValidationResponse[] } export async function handler(event: SQSEvent): Promise<string> { const promises = event.Records.map((record) => parseAndStoreSubscriptionUpdate(record, sqsRecordToAppleSubscription), ); return Promise.all(promises).then((_) => 'OK'); }