expo-iap 2.2.4-rc.2 → 2.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/iap.md CHANGED
@@ -142,17 +142,42 @@ This section describes purchase properties in `expo-iap`.
142
142
  | `id` | `string` | Purchased product ID |
143
143
  | `transactionId` | `string?` | Transaction ID (optional) |
144
144
  | `transactionDate` | `number` | Unix timestamp |
145
- | `purchaseTokenAndroid` | `string?` | Purchase token (optional) |
145
+ | `transactionReceipt` | `string` | Receipt data |
146
146
 
147
147
  ### Android-Only Purchase Types
148
148
 
149
- - **`ProductPurchase`**: Adds `ids`, `dataAndroid`, `signatureAndroid`, `purchaseStateAndroid`.
150
- - **`SubscriptionPurchase`**: Includes `autoRenewingAndroid`.
149
+ - **`ProductPurchase`**:
150
+
151
+ - Adds the following properties specific to in-app product purchases:
152
+ - **`ids`**: `string[]` - A list of product IDs associated with the purchase (for multi-item purchases).
153
+ - **`dataAndroid`**: `string` - The raw purchase data from Google Play (e.g., JSON payload).
154
+ - **`signatureAndroid`**: `string` - The cryptographic signature from Google Play to verify the purchase's authenticity.
155
+ - **`purchaseStateAndroid`**: `number` - The state of the purchase (e.g., 0 = purchased, 1 = canceled, 2 = pending).
156
+
157
+ - **`SubscriptionPurchase`**:
158
+
159
+ - Extends the base properties and includes:
160
+ - **`autoRenewingAndroid`**: `boolean` - Indicates whether the subscription automatically renews (true) or not (false).
161
+
162
+ - **`purchaseTokenAndroid`**:
163
+ - **Description**: A unique identifier provided by Google Play for each purchase, used to track, verify, and manage the transaction. For example, it is required to "consume" a consumable product (e.g., in-game coins) or query purchase details via the Google Play Developer API.
151
164
 
152
165
  ### iOS-Only Purchase Types
153
166
 
154
- - **`ProductPurchase`**: Adds fields like `quantityIos`, `expirationDateIos`, `subscriptionGroupIdIos`.
155
- - **`SubscriptionPurchase`**: Extends with subscription-specific handling.
167
+ - **`ProductPurchase`**:
168
+
169
+ - Extends the base purchase properties with iOS-specific fields:
170
+ - **`quantityIos`**: `number` - The quantity of the product purchased (e.g., how many units of an item were bought).
171
+ - **`expirationDateIos`**: `number?` - The expiration date of the purchase as a Unix timestamp (in milliseconds), if applicable (optional, may be null for non-expiring products).
172
+ - **`subscriptionGroupIdIos`**: `string?` - The identifier of the subscription group this product belongs to, used for managing related subscriptions in the App Store (optional, may be null for non-subscription products).
173
+
174
+ - **`SubscriptionPurchase`**:
175
+ - Extends the base purchase properties with iOS-specific subscription handling:
176
+ - Includes all fields from `ProductPurchase` where applicable, plus additional subscription-specific logic.
177
+ - May include fields like:
178
+ - **`expirationDateIos`**: `number?` - The date and time when the subscription expires, represented as a Unix timestamp (in milliseconds), unless auto-renewed.
179
+ - **`autoRenewingIos`**: `boolean` - Indicates whether the subscription is set to automatically renew (true) or not (false).
180
+ - Handles subscription-specific features such as renewals, grace periods, and App Store receipt validation.
156
181
 
157
182
  ## Implementation Notes
158
183
 
@@ -17,105 +17,84 @@ struct IapEvent {
17
17
 
18
18
  @available(iOS 15.0, *)
19
19
  func serializeTransaction(_ transaction: Transaction) -> [String: Any?] {
20
- // Determine if this is a subscription by productType or expirationDate
21
20
  let isSubscription =
22
21
  transaction.productType.rawValue.lowercased().contains("renewable")
23
22
  || transaction.expirationDate != nil
24
23
 
25
- // Parse transaction reason from jsonRepresentation if available
26
24
  var transactionReasonIos: String? = nil
27
25
  var webOrderLineItemId: Int? = nil
28
26
  var jsonData: [String: Any]? = nil
27
+ var jwsReceipt: String = ""
28
+
29
+ let jsonRep = transaction.jsonRepresentation
30
+ jwsReceipt = String(data: jsonRep, encoding: .utf8) ?? ""
29
31
 
30
- // JSON representation handling (JWS 데이터)
31
- var jwsReceipt: String
32
32
  do {
33
- let jsonRep = transaction.jsonRepresentation
34
- jwsReceipt = String(data: jsonRep, encoding: .utf8) ?? "" // JWS 형식으로 변환
35
- let jsonObj = try JSONSerialization.jsonObject(with: jsonRep) as! [String: Any]
36
- jsonData = jsonObj
37
- if let reason = jsonObj["transactionReason"] as? String {
38
- transactionReasonIos = reason
39
- }
40
- if let webOrderId = jsonObj["webOrderLineItemID"] as? NSNumber {
41
- webOrderLineItemId = webOrderId.intValue
33
+ if let jsonObj = try JSONSerialization.jsonObject(with: jsonRep) as? [String: Any] {
34
+ jsonData = jsonObj
35
+ transactionReasonIos = jsonObj["transactionReason"] as? String
36
+ if let webOrderId = jsonObj["webOrderLineItemID"] as? NSNumber {
37
+ webOrderLineItemId = webOrderId.intValue
38
+ }
42
39
  }
43
40
  } catch {
44
41
  print("Error parsing JSON representation: \(error)")
45
- jwsReceipt = ""
46
42
  }
47
43
 
48
- // Create base purchase object that matches Purchase type in TypeScript
49
44
  var purchaseMap: [String: Any?] = [
50
- // Core purchase fields
51
45
  "id": transaction.productID,
52
46
  "ids": [transaction.productID],
53
47
  "transactionId": String(transaction.id),
54
48
  "transactionDate": transaction.purchaseDate.timeIntervalSince1970 * 1000,
55
- "transactionReceipt": jwsReceipt, // JWS 데이터를 transactionReceipt에 담음
49
+ "transactionReceipt": jwsReceipt,
56
50
 
57
- // iOS specific fields - basic info
58
51
  "quantityIos": transaction.purchasedQuantity,
59
52
  "originalTransactionDateIos": transaction.originalPurchaseDate.timeIntervalSince1970 * 1000,
60
53
  "originalTransactionIdentifierIos": transaction.originalID,
61
54
  "appAccountToken": transaction.appAccountToken?.uuidString,
62
55
 
63
- // App and Product Identifiers
64
56
  "appBundleIdIos": transaction.appBundleID,
65
57
  "productTypeIos": transaction.productType.rawValue,
66
58
  "subscriptionGroupIdIos": transaction.subscriptionGroupID,
67
59
 
68
- // Transaction Identifiers
69
60
  "webOrderLineItemIdIos": webOrderLineItemId,
70
61
 
71
- // Purchase and Expiration Dates
72
62
  "expirationDateIos": transaction.expirationDate.map { $0.timeIntervalSince1970 * 1000 },
73
63
 
74
- // Purchase Details
75
64
  "isUpgradedIos": transaction.isUpgraded,
76
65
  "ownershipTypeIos": transaction.ownershipType.rawValue,
77
66
 
78
- // Revocation Status
79
67
  "revocationDateIos": transaction.revocationDate.map { $0.timeIntervalSince1970 * 1000 },
80
68
  "revocationReasonIos": transaction.revocationReason?.rawValue,
69
+ "transactionReasonIos": transactionReasonIos,
81
70
  ]
82
71
 
83
- // Environment (iOS 16.0+)
84
72
  if #available(iOS 16.0, *) {
85
73
  purchaseMap["environmentIos"] = transaction.environment.rawValue
86
74
  }
87
75
 
88
- // Storefront (iOS 17.0+)
89
76
  if #available(iOS 17.0, *) {
90
77
  purchaseMap["storefrontCountryCodeIos"] = transaction.storefront.countryCode
91
- }
92
-
93
- // Transaction Reason (iOS 17.0+)
94
- if #available(iOS 17.0, *) {
95
78
  purchaseMap["reasonIos"] = transaction.reason.rawValue
96
79
  }
97
80
 
98
- // reasonStringRepresentation은 Transaction에 없으므로 제거
99
- purchaseMap["transactionReasonIos"] = transactionReasonIos
100
-
101
- // Add offer information if available with proper availability check
102
81
  if #available(iOS 17.2, *) {
103
82
  if let offer = transaction.offer {
104
83
  purchaseMap["offerIos"] = [
105
- "id": offer.id as Any,
84
+ "id": offer.id,
106
85
  "type": offer.type.rawValue,
107
86
  "paymentMode": offer.paymentMode?.rawValue ?? "",
108
87
  ]
109
88
  }
110
89
  }
111
90
 
112
- // Add additional pricing info if available
113
- if #available(iOS 15.4, *), let priceInfo = jsonData?["price"] as? NSNumber {
114
- purchaseMap["priceIos"] = priceInfo.doubleValue
115
- }
116
-
117
- if #available(iOS 15.4, *), let currencyInfo = jsonData?["currency"] as? String {
118
- purchaseMap["currencyIos"] = currencyInfo
91
+ if #available(iOS 15.4, *), let jsonData = jsonData {
92
+ if let price = jsonData["price"] as? NSNumber {
93
+ purchaseMap["priceIos"] = price.doubleValue
94
+ }
95
+ if let currency = jsonData["currency"] as? String {
96
+ purchaseMap["currencyIos"] = currency
97
+ }
119
98
  }
120
99
 
121
100
  return purchaseMap
package/ios/Types.swift CHANGED
@@ -14,7 +14,6 @@ public enum StoreError: Error {
14
14
 
15
15
  enum IapErrors: String, CaseIterable {
16
16
  case E_UNKNOWN = "E_UNKNOWN"
17
- case E_NOT_INITIALIZED = "E_NOT_INITIALIZED"
18
17
  case E_SERVICE_ERROR = "E_SERVICE_ERROR"
19
18
  case E_USER_CANCELLED = "E_USER_CANCELLED"
20
19
  case E_USER_ERROR = "E_USER_ERROR"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-iap",
3
- "version": "2.2.4-rc.2",
3
+ "version": "2.2.4",
4
4
  "description": "In App Purchase module in Expo",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",