WWDC Quick Look 💓 By SwiftGGTeam
Support customers and handle refunds

Support customers and handle refunds

Watch original video

Highlight

StoreKit 2 NewshowManageSubscriptions()andbeginRefundRequest()The API allows users to manage subscriptions and apply for refunds within the app; the Consumption API allows developers to provide consumption data to Apple and participate in refund decisions.

Core Content

The user wants to cancel the subscription but cannot find the entrance. The user wants to apply for a refund but doesn’t know how. In these scenarios, users may leave negative reviews for the app or contact Apple support. If these issues could be addressed directly within the app, the user experience would be much better.

StoreKit 2 adds three new APIs this year to address these pain points.

Detailed Content

Manage subscriptions in-app

11:42

Previously, to manage subscriptions, users had to leave the app and go to the App Store. The admin page can now be opened directly within the app.

import StoreKit

// Show the subscription management page
func showSubscriptionManagement() {
    if let windowScene = view.window?.windowScene {
        Task {
            try await AppStore.showManageSubscriptions(in: windowScene)
        }
    }
}

12:20

Key points:

  • AppStore.showManageSubscriptions(in:)Show system management page
  • Users can see current subscription and renewal options
  • Can upgrade, downgrade or cancel your subscription
  • All operations are synced to the App Store

You can also display retention offers in front of your admin page:

// Show an offer before the user taps cancel
func showRetentionOffer() {
    // Show a custom retention UI
    // If the user still wants to cancel, call showManageSubscriptions
}

Apply for a refund within the app

13:36

Users can request a refund directly within the app without contacting Apple Support.

import StoreKit

// Request a refund
func requestRefund(for transaction: Transaction) async {
    do {
        let refundStatus = try await transaction.beginRefundRequest(
            in: view.window!.windowScene!
        )

        switch refundStatus {
        case .success:
            // Refund request has been submitted
            showRefundSubmittedMessage()
        case .userCancelled:
            // The user cancelled the request
            break
        }
    } catch {
        // Handle errors
        print("Refund request failed: \(error)")
    }
}

14:14

Key points:

  • transaction.beginRefundRequest(in:)Initiate a refund request
  • The system displays the refund reason selection interface
  • Refund approval is received by the serverREFUNDnotify
  • Server receives after refund is rejectedREFUND_DECLINEDnotify
  • Sandbox environment supports refund testing

Invoice Lookup API

03:44

The user contacted customer service and said “I bought something but didn’t receive it.” Customer service can ask the user to provide the order number (order ID) and then query it through the Invoice Lookup API.

GET https://api.storekit.itunes.apple.com/inApps/v1/lookup/{orderId}

return:

{
  "status": 0,
  "signedTransactions": [
    {
      "transactionId": "1000000123456789",
      "originalTransactionId": "1000000098765432",
      "productId": "com.example.coins",
      "purchaseDate": 1620000000000,
      "quantity": 100
    }
  ]
}

04:20

Key points:

  • status0 means valid, 1 means the order number is invalid, 2 means no matching transaction
  • Return signed transaction data in JWS format
  • Can verify transaction status and refund status
  • Help customer service quickly locate problems

Refund Lookup API

06:02

Query all refund records of a user:

GET https://api.storekit.itunes.apple.com/inApps/v1/refund/lookup/{originalTransactionId}

Suitable for processing server outage and then synchronizing refund data.

06:44

Key points:

  • useoriginalTransactionIdQuery
  • Return all refund transactions for this user
  • Suitable for complementary synchronization and auditing
  • Return JWS format data

Subscription Extension API

09:40

If there is a service failure (such as a live broadcast being interrupted), you can compensate by extending the user’s subscription for free.

PUT https://api.storekit.itunes.apple.com/inApps/v1/subscriptions/extend

{
  "originalTransactionId": "1000000098765432",
  "extensionDuration": 7,
  "extensionReason": 1,
  "requestIdentifier": "unique-request-id"
}

10:23

Key points:

  • Each subscription can be extended up to 2 times per year
  • Extended by up to 90 days at a time
  • Reason code required
  • User will receive email notification
  • The extension does not count towards the one-year period of 85% split

Consumption API

19:12

When a user requests a refund, Apple sends your serverCONSUMPTION_REQUESTnotify. You can reply with consumption data within 12 hours to help Apple make refund decisions.

PUT https://api.storekit.itunes.apple.com/inApps/v1/consumption/{originalTransactionId}

{
  "customerConsented": true,
  "consumptionStatus": 1,
  "consumptionPlatform": 1,
  "sampleContent": 1,
  "deliveryStatus": 0,
  "appAccountToken": "uuid-string",
  "accountTenure": 365,
  "playTime": 120,
  "lifetimeSpend": 99.99,
  "userStatus": 0
}

20:00

Key points:

  • customerConsented: Whether the user agrees to share data
  • consumptionStatus: 0=not consumed, 1=partially consumed, 2=fully consumed, 3=transferred
  • deliveryStatus:0=successfully delivered, 1=delivery failed
  • Must respond within 12 hours
  • Data helps Apple make more accurate refund decisions

Core Takeaways

  1. Provide subscription management entrance within the app. Users can view and modify subscriptions without leaving the app. Entrance API:AppStore.showManageSubscriptions(in:)

  2. Provide a refund application portal within the app. Reduce the chance of users leaving negative reviews or contacting Apple support. Entrance API:transaction.beginRefundRequest(in:)

  3. Use Invoice Lookup API to assist customer service. When users report a problem, they can use the order number to quickly check the transaction status. Entrance API:GET /inApps/v1/lookup/{orderId}

  4. Use Subscription Extension API to compensate for service failures. When the live broadcast is interrupted or the server fails, user subscriptions are extended for free. Entrance API:PUT /inApps/v1/subscriptions/extend

  5. Reply to Consumption Request and participate in refund decision. Provide consumption data to reduce unreasonable refunds. Entrance API:PUT /inApps/v1/consumption/{originalTransactionId}

Comments

GitHub Issues · utterances