Apple In-App Purchase
Payment plugin for Apple In-App Purchase: iOS apps purchase via StoreKit, the plugin validates receipts server-side against Apple's receipt verification service and tracks processed transactions to prevent duplicates.
Installation
Included in the all preset — registerAllPlugins() registers the plugin together with its webhook route and database module.
To register it individually:
import { pluginRegistry } from '@unchainedshop/core';
import { AppleIAPPlugin } from '@unchainedshop/plugins/payment/apple-iap';
pluginRegistry.register(AppleIAPPlugin);
Register before startPlatform(). At startup, the plugin adds the appleTransactions database module and enables POST /payment/apple-iap for App Store server notifications (path configurable via APPLE_IAP_WEBHOOK_PATH). The Express/Fastify connector mounts the route. If APPLE_IAP_SHARED_SECRET is missing, initialization logs a warning and skips this plugin's adapter and route; its database module has already been initialized.
Environment Variables
| Variable | Default | Description |
|---|---|---|
APPLE_IAP_SHARED_SECRET | - | App Store shared secret for receipt validation (required; the adapter and route are skipped without it) |
APPLE_IAP_ENVIRONMENT | sandbox | Receipt verification environment: sandbox or production |
APPLE_IAP_WEBHOOK_PATH | /payment/apple-iap | Server notification endpoint path |
Create Provider
mutation CreateAppleIAPProvider {
createPaymentProvider(
paymentProvider: {
type: GENERIC
adapterKey: "shop.unchained.apple-iap"
}
) {
_id
}
}
Payment Flow
The plugin does not support payment signing (signPaymentProviderForCheckout throws) — the purchase happens in the iOS app via StoreKit:
-
iOS app purchase: the user buys through StoreKit.
-
Register the receipt:
mutation RegisterReceipt {
registerPaymentCredentials(
paymentProviderId: "apple-iap-provider-id"
transactionContext: {
receiptData: "base64-encoded-receipt-data"
}
) {
_id
}
}
- Set the transaction identifier on the cart payment:
mutation UpdatePayment {
updateCartPaymentGeneric(
paymentProviderId: "apple-iap-provider-id"
meta: {
transactionIdentifier: "apple-transaction-id"
}
) {
_id
}
}
- Checkout:
mutation CheckoutCart {
checkoutCart(
paymentContext: {
receiptData: "base64-encoded-receipt-data" # optional if already registered
}
) {
_id
status
}
}
The charge validates the receipt with Apple, matches the transaction against the order, and rejects already-processed transactions.
Order Constraints
- Only one unique product per order.
- Order quantity must match the transaction quantity.
- The order's product ID must match the transaction's
product_id.
Testing
Use Apple's sandbox: keep APPLE_IAP_ENVIRONMENT=sandbox and test with sandbox App Store accounts and receipts.
Adapter Details
| Property | Value |
|---|---|
| Key | shop.unchained.apple-iap |
| Type | GENERIC |
| Version | 1.0.0 |
| Source | payment/apple-iap/ |