Sell physical goods and real-world services with native Apple Pay through Whop (2.7% + $0.30 fees)
Use Checkout.ApplePayButton to accept one-time payments for physical goods or real-world services using Apple Pay. Payments are processed by Whop at 2.7% + $0.30 instead of Apple’s 15–30% in-app purchase fees.
Apple Pay is not for digital content. Only use Checkout.ApplePayButton if your app sells physical goods (merch, food, hardware) or real-world services (event tickets, deliveries). Apps selling digital content — premium features, subscriptions, in-app currency, content unlocks — must use StoreKit. See the Apple Pay eligibility section for details.
Drop Checkout.ApplePayButton into your product view. The component renders Apple’s official, HIG-compliant Apple Pay button — you can’t substitute a custom-styled button.
import SwiftUIimport WhopCheckoutstruct ProductDetailView: View { let product: MerchItem @State private var purchaseComplete = false var body: some View { ScrollView { // product image, title, price, size picker... Checkout.ApplePayButton( // planId: find in Dashboard > Products > [product] > Plans planId: product.planId, label: .buy, onCompletion: { result in switch result { case .success(let purchase): // purchase.receiptId, purchase.membership purchaseComplete = true case .failure(let error): print("Purchase failed: \(error.localizedDescription)") } } ) .frame(height: 48) .padding(.horizontal) } }}
When the user taps, they get the native Apple Pay sheet. Face ID or double-click confirms, and the payment flows through Whop.
onCompletion is not called when the user cancels — the sheet just dismisses. No cleanup needed.
onCompletion receives a Result<CheckoutPurchaseResult, Error>. On success you get back:
Property
Type
Description
receiptId
String
Unique receipt ID for this purchase
membership
CheckoutMembership?
The membership Whop created for the purchase
Use this to show a confirmation screen, kick off shipping, or store the receipt for your own records.
onCompletion: { result in switch result { case .success(let purchase): // Persist the receipt for your fulfillment flow OrderStore.shared.record( receiptId: purchase.receiptId, membershipId: purchase.membership?.id, product: product ) showConfirmation = true case .failure(let error): showError(error.localizedDescription) }}
For physical goods, you’ll need a server-side webhook to trigger shipping after a successful purchase. Listen for the payment.succeeded event:
import { whopsdk } from "@/lib/whop-sdk";export async function POST(request: Request) { const body = await request.text(); const headers = Object.fromEntries(request.headers); const event = whopsdk.webhooks.unwrap(body, { headers }); if (event.type === "payment.succeeded") { // Trigger your shipping/fulfillment flow await fulfillOrder(event.data); } return new Response("OK", { status: 200 });}
The client-side onCompletion callback confirms the payment to the user, but your server should handle fulfillment via webhooks — don’t rely on the client alone. See the full webhooks guide for setup.