Skip to main content

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​

VariableDefaultDescription
APPLE_IAP_SHARED_SECRET-App Store shared secret for receipt validation (required; the adapter and route are skipped without it)
APPLE_IAP_ENVIRONMENTsandboxReceipt verification environment: sandbox or production
APPLE_IAP_WEBHOOK_PATH/payment/apple-iapServer 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:

  1. iOS app purchase: the user buys through StoreKit.

  2. Register the receipt:

mutation RegisterReceipt {
registerPaymentCredentials(
paymentProviderId: "apple-iap-provider-id"
transactionContext: {
receiptData: "base64-encoded-receipt-data"
}
) {
_id
}
}
  1. Set the transaction identifier on the cart payment:
mutation UpdatePayment {
updateCartPaymentGeneric(
paymentProviderId: "apple-iap-provider-id"
meta: {
transactionIdentifier: "apple-transaction-id"
}
) {
_id
}
}
  1. 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​

PropertyValue
Keyshop.unchained.apple-iap
TypeGENERIC
Version1.0.0
Sourcepayment/apple-iap/