@@ -70,7 +70,39 @@ public enum OpenIapSerialization {
7070 }
7171
7272 public static func purchaseInput( from object: Any ) throws -> PurchaseInput {
73- try decode ( object: object, as: PurchaseInput . self)
73+ guard let dict = object as? [ String : Any ] else {
74+ print ( " ❌ [OpenIapSerialization] purchaseInput: object is not a dictionary - \( type ( of: object) ) " )
75+ throw PurchaseError . make ( code: . developerError, message: " Purchase must be a dictionary " )
76+ }
77+
78+ print ( " 📦 [OpenIapSerialization] purchaseInput received dict keys: \( dict. keys. sorted ( ) ) " )
79+
80+ // Check if already wrapped as Purchase union
81+ if dict [ " purchaseIos " ] != nil || dict [ " purchaseAndroid " ] != nil {
82+ print ( " ✅ [OpenIapSerialization] Already wrapped as Purchase union " )
83+ // Already wrapped, decode as-is
84+ return try decode ( object: dict, as: PurchaseInput . self)
85+ }
86+
87+ print ( " 🔄 [OpenIapSerialization] Auto-wrapping as iOS purchase " )
88+
89+ // Map common field aliases for backward compatibility
90+ var normalizedDict = dict
91+ if normalizedDict [ " transactionId " ] == nil , let id = normalizedDict [ " id " ] {
92+ normalizedDict [ " transactionId " ] = id
93+ }
94+
95+ // Decode as PurchaseIOS first, then wrap in Purchase enum
96+ do {
97+ let purchaseIOS = try decode ( object: normalizedDict, as: PurchaseIOS . self)
98+ let result = Purchase . purchaseIos ( purchaseIOS)
99+ print ( " ✅ [OpenIapSerialization] Successfully decoded and wrapped PurchaseInput " )
100+ return result
101+ } catch {
102+ print ( " ❌ [OpenIapSerialization] Decode failed: \( error) " )
103+ print ( " 📦 [OpenIapSerialization] Dict keys: \( dict. keys. sorted ( ) ) " )
104+ throw error
105+ }
74106 }
75107
76108 public static func purchaseOptions( from object: Any ) throws -> PurchaseOptions {
0 commit comments