Apple App Store Server API Client
This package provides a robust Node.js client for the Apple App Store Server API, simplifying interactions with Apple's services for in-app purchases and subscriptions. Currently stable at version 0.17.3, it receives frequent updates, often aligning with new releases and features of the underlying App Store Server API. Key features include automatic management of authentication tokens, comprehensive support for App Store Server Notifications V2, typed responses for enhanced developer experience, and helpers for decoding JWS (JSON Web Signature) items with built-in certificate validation against Apple's Certificate Authority. It distinguishes itself by abstracting away much of the complexity of the Apple API, including handling of transaction history, subscription status, and order lookup endpoints, while providing type-safe parameters for filtering and sorting.
Common errors
-
Error: Unexpected status code: 202 Accepted
cause Older versions of the client did not explicitly handle HTTP 202 (Accepted) as a successful response for certain API calls, leading to errors.fixUpgrade `app-store-server-api` to version `0.17.1` or newer, which includes handling for the 202 status code. -
SyntaxError: Cannot use import statement outside a module or ReferenceError: require is not defined
cause Attempting to use ES module `import` syntax in a CommonJS context or `require()` in an ES module context, especially prevalent after the v0.17.0 TypeScript target change to ES6.fixEnsure your project is configured for ES modules (e.g., by setting `"type": "module"` in `package.json` or using `.mjs` file extension) and use `import` statements. Alternatively, if sticking to CommonJS, explicitly use `require()` syntax with `.cjs` file extension or configure `tsconfig.json` for CommonJS output. -
Error: API request failed with status 401: NOT_AUTHORIZED
cause The JWT (JSON Web Token) used for authentication is invalid, expired, or the credentials (Key ID, Issuer ID, Bundle ID, Private Key) are incorrect.fixDouble-check your API Key, Key ID, Issuer ID, and App Bundle ID for accuracy. Ensure the private key is correctly formatted and the JWT generation logic is sound. Verify that the JWT is not expired and is signed with the correct algorithm (ES256).
Warnings
- breaking As of `v0.16.0`, the library requires Node.js version 18.12.0 or newer. Users running older Node.js runtimes must upgrade their environment to avoid compatibility issues.
- breaking In `v0.17.0`, the TypeScript compilation target was changed to ES6. This may impact projects with specific `tsconfig.json` configurations or build processes that rely on an older TypeScript output target.
- gotcha Apple's official documentation for obtaining the Issuer ID in App Store Connect is currently incorrect. Developers often struggle to find it.
- gotcha A typo in a notification helper function, `isdecodedNotificationSummaryPayload`, was fixed in `v0.14.1`. The correct name is `isDecodedNotificationSummaryPayload`.
Install
-
npm install app-store-server-api -
yarn add app-store-server-api -
pnpm add app-store-server-api
Imports
- AppStoreServerAPI
const AppStoreServerAPI = require('app-store-server-api').AppStoreServerAPIimport { AppStoreServerAPI } from 'app-store-server-api' - Environment
import { ENVIRONMENT } from 'app-store-server-api'import { Environment } from 'app-store-server-api' - decodeTransaction
import decodeTransaction from 'app-store-server-api/decodeTransaction'
import { decodeTransaction } from 'app-store-server-api' - isDecodedNotificationSummaryPayload
import { isdecodedNotificationSummaryPayload } from 'app-store-server-api'import { isDecodedNotificationSummaryPayload } from 'app-store-server-api'
Quickstart
import { AppStoreServerAPI, Environment, ProductTypeParameter, SortParameter } from 'app-store-server-api';
// Ensure these are loaded from secure environment variables in a production setup
const KEY = process.env.APPLE_PRIVATE_KEY ??
`-----BEGIN PRIVATE KEY-----
MHcCAQEEIPWH5lyoG7Wbzv71ntF6jNvFwwJLKYmPWN/KBD4qJfMcoAoGCCqGSM49
AwEHoUQDQgAEMOlUa/hmyAPU/RUBds6xzDO8QNrTFhFwzm8E4wxDnSAx8R9WOMnD
cVGdtnbLFIdLk8g4S7oAfV/gGILKuc+Vqw==
-----END PRIVATE KEY-----`;
const KEY_ID = process.env.APPLE_KEY_ID ?? "ABCD123456";
const ISSUER_ID = process.env.APPLE_ISSUER_ID ?? "91fa5999-7b54-4363-a2a8-265363fa6cbe";
const APP_BUNDLE_ID = process.env.APPLE_APP_BUNDLE_ID ?? "com.yourcompany.app";
const api = new AppStoreServerAPI(
KEY, KEY_ID, ISSUER_ID, APP_BUNDLE_ID, Environment.Sandbox // Use Environment.Production for live apps
);
async function getRecentPurchases(originalTransactionId: string) {
try {
const response = await api.getTransactionHistory(originalTransactionId, {
productType: ProductTypeParameter.AutoRenewable,
sort: SortParameter.Descending,
limit: 20 // Fetch up to 20 transactions
});
// Decoding verifies the signature and provides typed access to transaction data.
const transactions = await api.decodeTransactions(response.signedTransactions);
console.log(`Found ${transactions.length} transactions for ${originalTransactionId}:`);
for (const transaction of transactions) {
console.log(` Transaction ID: ${transaction.transactionId}, Product ID: ${transaction.productId}, Purchase Date: ${new Date(transaction.purchaseDate).toISOString()}`);
// Further processing with transaction data...
}
if (response.hasMore) {
console.log('More transactions available. Consider fetching with response.revision for next page.');
}
} catch (error) {
console.error('Error fetching transaction history:', error);
}
}
// Example usage (replace with a real originalTransactionId from your system)
// In a real application, you'd get this from your database or client.
const exampleOriginalTransactionId = "123456789012345";
getRecentPurchases(exampleOriginalTransactionId);