Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2026 App ServicesApp Store, Distribution & Marketing

WWDC26 · 13 min · App Services / App Store, Distribution & Marketing

What’s new in Apple In-App Purchase

Discover how monthly subscriptions with a 12-month commitment give people a more affordable option to pay for your subscription and secure a longer-term commitment. Explore how to configure and test this new payment option using App Store Connect, StoreKit APIs, Xcode testing, and more. Plus, learn about improvements to offer code redemption APIs, and enhancements to the App Review submission experience.

Watch at developer.apple.com ↗

Transcript all transcripts

Chapters

  • 0:01 — Introduction
  • 0:51 — Overview of monthly subscriptions with a 12-month commitment
  • 1:42 — Set up in App Store Connect
  • 2:28 — Merchandise with StoreKit
  • 6:55 — Monitor subscriptions with App Store Server APIs
  • 8:50 — Bundles and Suites
  • 9:26 — Offer code redemption
  • 10:35 — Enhanced submission experience
  • 12:38 — Next steps

Code shown on screen · 6 snippets

Merchandise pricing terms with StoreKit views swift · at 3:29 ↗
// Merchandise pricing terms with StoreKit views

import StoreKit
import SwiftUI

struct SubscriptionStore: View {
    var body: some View {
        SubscriptionStoreView(groupID: "3F19ED53") {
            // Custom marketing content
        }
        .preferredSubscriptionPricingTerms {_, subscriptionInfo in
            subscriptionInfo.pricingTerms.first {
                $0.billingPlanType == .monthly
            }
        }
    }
}
Get subscription pricing terms and make a purchase swift · at 4:02 ↗
// Get subscription pricing terms and make a purchase

import StoreKit

var product: Product?
// Fetch and assign product

// Get the monthly billing plan's pricing terms for merchandising
let pricingTerms = product?.subscription?.pricingTerms
  .first(where: {$0.billingPlanType == .monthly })
if let pricingTerms {
  let monthlyPrice = pricingTerms.billingDisplayPrice
  let totalCommitmentPrice = pricingTerms.commitmentInfo.price
  // Display both monthly and total commitment price to the customer
}

let result = try? await product?.purchase(options: [.billingPlanType(.monthly)])
switch result {
  // Verify the transaction, give the customer access to
  // the purchased content, and then finish the transaction
}
Sheet to manage subscriptions by subscriptionGroupID swift · at 5:05 ↗
// Sheet to manage subscriptions by subscriptionGroupID

import SwiftUI
import StoreKit

struct ManageSubscriptionsButton: View {
    let subscriptionGroupID: String
    @State var presentingManageSubscriptionsSheet: Bool = false

    var body: some View {
        Button("Manage Subscriptions") {
            presentingManageSubscriptionsSheet = true
        }
        .manageSubscriptionsSheet(
            isPresented: $presentingManageSubscriptionsSheet,
            subscriptionGroupID: subscriptionGroupID
        )
    }
}
JWSTransaction (decoded) for a monthly subscription with a 12-month commitment json · at 7:45 ↗
// JWSTransaction (decoded) for a monthly subscription with a 12-month commitment

{
    // …
    "expiresDate": 1783503660000, // for this billing period
    "price": 10990, // for this billing period
    "productId": "plus.pro.annual",
    "purchaseDate": 1780911660000,
    "type": "Auto-Renewable Subscription",
    "billingPlanType": "MONTHLY",
    "commitmentInfo": {
        "billingPeriodNumber": 1,
        "totalBillingPeriods": 12,
        "commitmentExpiresDate": 1812447660000,
        "commitmentPrice": 131880,
    }
}
JWSRenewalInfo (decoded) for a monthly subscription with a 12-month commitment json · at 7:59 ↗
// JWSRenewalInfo (decoded) for a monthly subscription with a 12-month commitment

{
    // … 
    "renewalBillingPlanType": "MONTHLY",
    "commitmentInfo": {
        "commitmentAutoRenewProductId": “plus.standard.annual”,
        "commitmentAutoRenewStatus": 0,
        "commitmentRenewalDate": 1812447660000,
        "commitmentRenewalPrice": 10990,
        "commitmentRenewalBillingPlanType": "BILLED_UPFRONT"
    }
}
Sheet to redeem an offer code swift · at 9:58 ↗
// Sheet to redeem an offer code

struct OfferCodeRedemption: View {
    @State var presentingOfferCodeSheet: Bool = false

    var body: some View {
        Button("Redeem Offer Code") {
            presentingOfferCodeSheet = true
        }
        .offerCodeRedemption(options: [], isPresented: $presentingOfferCodeSheet) {result in
            switch result {
            case .success(let verificationResult):
                switch verificationResult {
                    // Verify the transaction, give the customer access to
                    // the purchased content, and then finish the transaction
                }
            case .failure(let error):
                // Handle error
            }
        }
    }
}

Resources