@@ -84,6 +84,9 @@ enum StoreKitTypesBridge {
8484 offerInfo = nil
8585 }
8686
87+ let ownershipDescription = ownershipTypeDescription ( from: transaction. ownershipType)
88+ let reasonDetails = transactionReasonDetails ( from: transaction)
89+
8790 return PurchaseIOS (
8891 appAccountToken: transaction. appAccountToken? . uuidString,
8992 appBundleIdIOS: transaction. appBundleID,
@@ -105,15 +108,15 @@ enum StoreKitTypesBridge {
105108 offerIOS: offerInfo,
106109 originalTransactionDateIOS: transaction. originalPurchaseDate. milliseconds,
107110 originalTransactionIdentifierIOS: transaction. originalID != 0 ? String ( transaction. originalID) : nil ,
108- ownershipTypeIOS: transaction . ownershipType . description ,
111+ ownershipTypeIOS: ownershipDescription ,
109112 platform: . ios,
110113 productId: transaction. productID,
111114 purchaseState: purchaseState,
112115 purchaseToken: jwsRepresentation ?? transactionId,
113116 quantity: transaction. purchasedQuantity,
114117 quantityIOS: transaction. purchasedQuantity,
115- reasonIOS: transaction . reasonDescription ,
116- reasonStringRepresentationIOS: transaction . reasonDescription ,
118+ reasonIOS: reasonDetails . lowercased ,
119+ reasonStringRepresentationIOS: reasonDetails . string ,
117120 revocationDateIOS: revocationDate,
118121 revocationReasonIOS: transaction. revocationReason? . rawValue. description,
119122 storefrontCountryCodeIOS: {
@@ -126,7 +129,7 @@ enum StoreKitTypesBridge {
126129 subscriptionGroupIdIOS: transaction. subscriptionGroupID,
127130 transactionDate: transaction. purchaseDate. milliseconds,
128131 transactionId: transactionId,
129- transactionReasonIOS: transaction . transactionReason ?? " PURCHASE " ,
132+ transactionReasonIOS: reasonDetails . uppercased ,
130133 webOrderLineItemIdIOS: transaction. webOrderLineItemID. map { String ( $0) }
131134 )
132135 }
@@ -298,6 +301,56 @@ private extension StoreKitTypesBridge {
298301 type: String ( describing: offer. type)
299302 )
300303 }
304+
305+ static func ownershipTypeDescription( from ownership: StoreKit . Transaction . OwnershipType ) -> String {
306+ switch ownership {
307+ case . purchased:
308+ return " purchased "
309+ case . familyShared:
310+ return " family_shared " // Maintain backward compatibility
311+ default :
312+ return " purchased " // Default to purchased for compatibility
313+ }
314+ }
315+
316+ struct TransactionReason {
317+ let lowercased : String
318+ let string : String
319+ let uppercased : String
320+ }
321+
322+ static func transactionReasonDetails( from transaction: StoreKit . Transaction ) -> TransactionReason {
323+ if let revocation = transaction. revocationReason {
324+ // Map revocation reasons to expected strings
325+ let reasonString : String
326+ switch revocation {
327+ case . developerIssue:
328+ reasonString = " developer_issue "
329+ case . other:
330+ reasonString = " other "
331+ default :
332+ reasonString = " unknown "
333+ }
334+ return TransactionReason (
335+ lowercased: reasonString,
336+ string: reasonString,
337+ uppercased: reasonString. uppercased ( )
338+ )
339+ }
340+
341+ if transaction. isUpgraded {
342+ return TransactionReason ( lowercased: " upgrade " , string: " upgrade " , uppercased: " UPGRADE " )
343+ }
344+
345+ // Try to infer renewal for iOS <17
346+ if transaction. productType == . autoRenewable,
347+ let expirationDate = transaction. expirationDate,
348+ expirationDate > transaction. purchaseDate {
349+ return TransactionReason ( lowercased: " renewal " , string: " renewal " , uppercased: " RENEWAL " )
350+ }
351+
352+ return TransactionReason ( lowercased: " purchase " , string: " purchase " , uppercased: " PURCHASE " )
353+ }
301354}
302355
303356@available ( iOS 15 . 0 , macOS 14 . 0 , * )
@@ -343,50 +396,6 @@ private extension StoreKit.Product.SubscriptionPeriod.Unit {
343396 }
344397}
345398
346- @available ( iOS 15 . 0 , macOS 14 . 0 , * )
347- private extension Date {
399+ extension Date {
348400 var milliseconds : Double { timeIntervalSince1970 * 1000 }
349401}
350-
351- @available ( iOS 15 . 0 , macOS 14 . 0 , * )
352- private extension StoreKit . Transaction {
353- var reasonDescription : String ? {
354- if #available( iOS 17 . 0 , macOS 14 . 0 , * ) {
355- switch reason {
356- case . purchase: return " purchase "
357- case . renewal: return " renewal "
358- default : return " unknown "
359- }
360- }
361- return nil
362- }
363-
364- var transactionReason : String ? {
365- if #available( iOS 17 . 0 , macOS 14 . 0 , * ) {
366- switch reason {
367- case . purchase: return " PURCHASE "
368- case . renewal: return " RENEWAL "
369- default : return " UNKNOWN "
370- }
371- }
372- return nil
373- }
374- }
375-
376- @available ( iOS 15 . 0 , macOS 14 . 0 , * )
377- private extension StoreKit . Transaction . OwnershipType {
378- var description : String {
379- switch self {
380- case . purchased: return " purchased "
381- case . familyShared: return " family_shared "
382- default : return " purchased "
383- }
384- }
385- }
386-
387- @available ( iOS 15 . 0 , macOS 14 . 0 , * )
388- extension RequestPurchaseIosProps {
389- func storeKitPurchaseOptions( ) -> Set < StoreKit . Product . PurchaseOption > {
390- StoreKitTypesBridge . purchaseOptions ( from: self )
391- }
392- }
0 commit comments