Skip to content

Commit d472256

Browse files
authored
[in_app_purchase_storekit] Add storekit 2 support for canMakePayments and products (#7473)
This PR contains support for StoreKit2's [canMakePayments](https://developer.apple.com/documentation/storekit/appstore/3822277-canmakepayments) and [products](https://developer.apple.com/documentation/storekit/product/3851116-products). This also contains basic scaffolding for SK2 support, such as pigeon translators. Part of flutter/flutter#116383
1 parent c765f57 commit d472256

20 files changed

+2146
-7
lines changed

packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.3.18+1
2+
3+
* Adds support for StoreKit2's `canMakePayments` and `products`
4+
15
## 0.3.18
26

37
* Updates minimum supported SDK version to Flutter 3.19/Dart 3.3.

packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI {
4141
registrar.addMethodCallDelegate(instance, channel: channel)
4242
registrar.addApplicationDelegate(instance)
4343
SetUpInAppPurchaseAPI(messenger, instance)
44+
if #available(iOS 15.0, macOS 12.0, *) {
45+
InAppPurchase2APISetup.setUp(binaryMessenger: messenger, api: instance)
46+
}
4447
}
4548

4649
// This init is used for tests
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
@available(iOS 15.0, macOS 12.0, *)
6+
extension InAppPurchasePlugin: InAppPurchase2API {
7+
// MARK: - Pigeon Functions
8+
9+
// Wrapper method around StoreKit2's canMakePayments() method
10+
// https://developer.apple.com/documentation/storekit/appstore/3822277-canmakepayments
11+
func canMakePayments() throws -> Bool {
12+
return AppStore.canMakePayments
13+
}
14+
15+
// Wrapper method around StoreKit2's products() method
16+
// https://developer.apple.com/documentation/storekit/product/3851116-products
17+
func products(
18+
identifiers: [String], completion: @escaping (Result<[SK2ProductMessage], Error>) -> Void
19+
) {
20+
Task {
21+
do {
22+
let products = try await Product.products(for: identifiers)
23+
let productMessages = products.map {
24+
$0.convertToPigeon
25+
}
26+
completion(.success(productMessages))
27+
} catch {
28+
completion(
29+
.failure(
30+
PigeonError(
31+
code: "storekit2_products_error",
32+
message: error.localizedDescription,
33+
details: error.localizedDescription)))
34+
}
35+
}
36+
}
37+
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import Foundation
6+
import StoreKit
7+
8+
@available(iOS 15.0, macOS 12.0, *)
9+
extension Product {
10+
var convertToPigeon: SK2ProductMessage {
11+
12+
return SK2ProductMessage(
13+
id: id,
14+
displayName: displayName,
15+
description: description,
16+
price: NSDecimalNumber(decimal: price).doubleValue,
17+
displayPrice: displayPrice,
18+
type: type.convertToPigeon,
19+
subscription: subscription?.convertToPigeon,
20+
priceLocale: priceFormatStyle.locale.convertToPigeon
21+
)
22+
}
23+
}
24+
25+
extension SK2ProductMessage: Equatable {
26+
static func == (lhs: SK2ProductMessage, rhs: SK2ProductMessage) -> Bool {
27+
return lhs.id == rhs.id && lhs.displayName == rhs.displayName
28+
&& lhs.description == rhs.description && lhs.price == rhs.price
29+
&& lhs.displayPrice == rhs.displayPrice && lhs.type == rhs.type
30+
&& lhs.subscription == rhs.subscription && lhs.priceLocale == rhs.priceLocale
31+
}
32+
}
33+
34+
@available(iOS 15.0, macOS 12.0, *)
35+
extension Product.ProductType {
36+
var convertToPigeon: SK2ProductTypeMessage {
37+
switch self {
38+
case Product.ProductType.autoRenewable:
39+
return SK2ProductTypeMessage.autoRenewable
40+
case Product.ProductType.consumable:
41+
return SK2ProductTypeMessage.consumable
42+
case Product.ProductType.nonConsumable:
43+
return SK2ProductTypeMessage.nonConsumable
44+
case Product.ProductType.nonRenewable:
45+
return SK2ProductTypeMessage.nonRenewable
46+
default:
47+
fatalError("An unknown ProductType was passed in")
48+
}
49+
}
50+
}
51+
52+
@available(iOS 15.0, macOS 12.0, *)
53+
extension Product.SubscriptionInfo {
54+
var convertToPigeon: SK2SubscriptionInfoMessage {
55+
return SK2SubscriptionInfoMessage(
56+
promotionalOffers: promotionalOffers.map({ $0.convertToPigeon }),
57+
subscriptionGroupID: subscriptionGroupID,
58+
subscriptionPeriod: subscriptionPeriod.convertToPigeon)
59+
}
60+
}
61+
62+
extension SK2SubscriptionInfoMessage: Equatable {
63+
static func == (lhs: SK2SubscriptionInfoMessage, rhs: SK2SubscriptionInfoMessage) -> Bool {
64+
return lhs.promotionalOffers == rhs.promotionalOffers
65+
&& lhs.subscriptionGroupID == rhs.subscriptionGroupID
66+
&& lhs.subscriptionPeriod == rhs.subscriptionPeriod
67+
}
68+
}
69+
70+
@available(iOS 15.0, macOS 12.0, *)
71+
extension Product.SubscriptionOffer {
72+
var convertToPigeon: SK2SubscriptionOfferMessage {
73+
return SK2SubscriptionOfferMessage(
74+
/// ID is always `nil` for introductory offers and never `nil` for other offer types.
75+
id: id,
76+
price: NSDecimalNumber(decimal: price).doubleValue,
77+
type: type.convertToPigeon,
78+
period: period.convertToPigeon,
79+
periodCount: Int64(periodCount),
80+
paymentMode: paymentMode.convertToPigeon
81+
)
82+
}
83+
}
84+
85+
extension SK2SubscriptionOfferMessage: Equatable {
86+
static func == (lhs: SK2SubscriptionOfferMessage, rhs: SK2SubscriptionOfferMessage) -> Bool {
87+
return lhs.id == rhs.id && lhs.price == rhs.price && lhs.type == rhs.type
88+
&& lhs.period == rhs.period && lhs.periodCount == rhs.periodCount
89+
&& lhs.paymentMode == rhs.paymentMode
90+
}
91+
}
92+
93+
@available(iOS 15.0, macOS 12.0, *)
94+
extension Product.SubscriptionOffer.OfferType {
95+
var convertToPigeon: SK2SubscriptionOfferTypeMessage {
96+
switch self {
97+
case .introductory:
98+
return SK2SubscriptionOfferTypeMessage.introductory
99+
case .promotional:
100+
return SK2SubscriptionOfferTypeMessage.promotional
101+
default:
102+
fatalError("An unknown OfferType was passed in")
103+
}
104+
}
105+
}
106+
107+
@available(iOS 15.0, macOS 12.0, *)
108+
extension Product.SubscriptionPeriod {
109+
var convertToPigeon: SK2SubscriptionPeriodMessage {
110+
return SK2SubscriptionPeriodMessage(
111+
value: Int64(value),
112+
unit: unit.convertToPigeon)
113+
}
114+
}
115+
116+
extension SK2SubscriptionPeriodMessage: Equatable {
117+
static func == (lhs: SK2SubscriptionPeriodMessage, rhs: SK2SubscriptionPeriodMessage) -> Bool {
118+
return lhs.value == rhs.value && lhs.unit == rhs.unit
119+
}
120+
}
121+
122+
@available(iOS 15.0, macOS 12.0, *)
123+
extension Product.SubscriptionPeriod.Unit {
124+
var convertToPigeon: SK2SubscriptionPeriodUnitMessage {
125+
switch self {
126+
case .day:
127+
return SK2SubscriptionPeriodUnitMessage.day
128+
case .week:
129+
return SK2SubscriptionPeriodUnitMessage.week
130+
case .month:
131+
return SK2SubscriptionPeriodUnitMessage.month
132+
case .year:
133+
return SK2SubscriptionPeriodUnitMessage.year
134+
@unknown default:
135+
fatalError("unknown SubscriptionPeriodUnit encountered")
136+
}
137+
}
138+
}
139+
140+
@available(iOS 15.0, macOS 12.0, *)
141+
extension Product.SubscriptionOffer.PaymentMode {
142+
var convertToPigeon: SK2SubscriptionOfferPaymentModeMessage {
143+
switch self {
144+
case .freeTrial:
145+
return SK2SubscriptionOfferPaymentModeMessage.freeTrial
146+
case .payUpFront:
147+
return SK2SubscriptionOfferPaymentModeMessage.payUpFront
148+
case .payAsYouGo:
149+
return SK2SubscriptionOfferPaymentModeMessage.payAsYouGo
150+
default:
151+
fatalError("Encountered an unknown PaymentMode")
152+
}
153+
}
154+
}
155+
156+
extension Locale {
157+
var convertToPigeon: SK2PriceLocaleMessage {
158+
return SK2PriceLocaleMessage(
159+
currencyCode: currencyCode ?? "",
160+
currencySymbol: currencySymbol ?? ""
161+
)
162+
}
163+
}
164+
165+
extension SK2PriceLocaleMessage: Equatable {
166+
static func == (lhs: SK2PriceLocaleMessage, rhs: SK2PriceLocaleMessage) -> Bool {
167+
return lhs.currencyCode == rhs.currencyCode && lhs.currencySymbol == rhs.currencySymbol
168+
}
169+
}

0 commit comments

Comments
 (0)