iap-apple
Version:
Integration with Apples InAppPurchases in Typescript, available for NodeJS environments.
234 lines (171 loc) • 6.53 kB
Markdown
| Statements | Branches | Functions | Lines |
| --------------------------- | ----------------------- | ------------------------- | ----------------- |
|  |  |  |  |
# iap-apple
[](https://www.npmjs.com/package/iap-apple)
[](https://www.npmjs.com/package/iap-apple)
[](https://github.com/ssbarbee/iap-apple/issues)
[](https://opensource.org/licenses/ISC)
**Lightweight Apple App Store receipt validation for Node.js** - Zero dependencies, TypeScript-first, blazing fast.
## Why iap-apple? 🤔
| Feature | iap-apple | Others |
|---------|-----------|--------|
| Runtime Dependencies | **0** | 5-10+ |
| TypeScript | **Native** | Partial/None |
| Bundle Size | **~15KB** | 100KB+ |
| Node.js Fetch | **Native** | axios/request |
| Maintained | **2024+** | Often stale |
- **Zero Dependencies** - Uses Node.js native `fetch`, no bloat
- **TypeScript-First** - Full type definitions, great IDE support
- **Production Ready** - 93%+ test coverage, battle-tested
- **Simple API** - One function to validate, intuitive helpers
## Installation 📦
```bash
# pnpm (recommended)
pnpm add iap-apple
# npm
npm install iap-apple
# yarn
yarn add iap-apple
```
**Requirements:** Node.js 22+
## Quick Start 🚀
```typescript
import { verify, getPurchasedItems, isPurchasedItemExpired } from 'iap-apple';
// Validate a receipt
const response = await verify(receiptData, {
appSharedSecret: 'your-shared-secret',
});
// Get purchased items (sorted by date, deduplicated)
const items = getPurchasedItems(response);
// Check subscription status
const latestPurchase = items[0];
if (!isPurchasedItemExpired(latestPurchase)) {
console.log('Subscription is active!');
}
```
## API Reference 📚
### verify(receipt, config)
Validates a receipt against Apple's verifyReceipt endpoint. Automatically handles production/sandbox fallback.
```typescript
import { verify, IAPAppleError } from 'iap-apple';
try {
const response = await verify(receiptData, {
// Required: Your app's shared secret from App Store Connect
appSharedSecret: 'your-shared-secret',
// Optional: Exclude old transactions (default: false)
appleExcludeOldTransactions: true,
// Optional: Force sandbox environment (default: false)
test: false,
// Optional: Debug logging
logger: console,
});
console.log('Receipt validated:', response.status === 0);
} catch (error) {
const { rejectionMessage, data } = error as IAPAppleError;
console.error('Validation failed:', rejectionMessage);
}
```
### isVerifiedReceipt(response)
Check if a receipt validation was successful.
```typescript
import { verify, isVerifiedReceipt } from 'iap-apple';
const response = await verify(receipt, config);
if (isVerifiedReceipt(response)) {
// Receipt is valid
}
```
### getPurchasedItems(response)
Extract purchased items from the response. Returns items sorted by purchase date (newest first), deduplicated by `original_transaction_id`.
```typescript
import { verify, getPurchasedItems } from 'iap-apple';
const response = await verify(receipt, config);
const items = getPurchasedItems(response);
for (const item of items) {
console.log(`Product: ${item.productId}`);
console.log(`Purchased: ${new Date(item.purchaseDateMS)}`);
console.log(`Expires: ${item.expirationDateMS ? new Date(item.expirationDateMS) : 'Never'}`);
}
```
### isPurchasedItemExpired(item)
Check if a subscription has expired or been cancelled.
```typescript
import { getPurchasedItems, isPurchasedItemExpired } from 'iap-apple';
const items = getPurchasedItems(response);
const subscription = items[0];
if (isPurchasedItemExpired(subscription)) {
console.log('Subscription expired or cancelled');
} else {
console.log('Subscription is active');
}
```
### isPurchasedItemCanceled(item)
Check if a purchase was cancelled (refunded).
```typescript
import { getPurchasedItems, isPurchasedItemCanceled } from 'iap-apple';
const items = getPurchasedItems(response);
if (isPurchasedItemCanceled(items[0])) {
console.log('User received a refund');
}
```
## Types 📝
### PurchasedItem
```typescript
interface PurchasedItem {
bundleId: string;
appItemId: string;
transactionId: string;
originalTransactionId?: string;
productId: string;
purchaseDateMS: number;
originalPurchaseDateMS?: number;
expirationDateMS?: number;
cancellationDateMS?: number;
isTrialPeriod: boolean;
quantity: number;
}
```
### IIAPAppleConfig
```typescript
interface IIAPAppleConfig {
appSharedSecret: string; // Required
appleExcludeOldTransactions?: boolean; // Default: false
test?: boolean; // Default: false
logger?: ILogger | null; // Default: null
}
```
### Error Handling
All errors are thrown as `IAPAppleError`:
```typescript
interface IAPAppleError {
rejectionMessage: string;
data?: IVerifyReceiptResponseBody | null;
}
```
Common status codes:
- `21002` - Malformed receipt data
- `21003` - Receipt authentication failed
- `21004` - Shared secret mismatch
- `21005` - Apple server unavailable
- `21006` - Subscription expired
- `21007` - Sandbox receipt sent to production
- `21008` - Production receipt sent to sandbox
## StoreKit 2 / App Store Server API 🆕
This library uses Apple's legacy `verifyReceipt` endpoint, which still works but is deprecated for new apps.
For new projects using **StoreKit 2**, consider Apple's official library:
```bash
npm install @apple/app-store-server-library
```
**When to use iap-apple:**
- Existing apps using StoreKit 1
- Need zero dependencies
- Want a simpler API
- Validating receipts from older iOS versions
**When to use Apple's library:**
- New apps with StoreKit 2
- Need App Store Server Notifications V2
- Need subscription offer signing
## Contributing 🤝
Contributions are welcome! Please open an issue or submit a PR.
## License 📄
ISC