11use serde:: de:: DeserializeOwned ;
2+ use tauri:: Manager ;
23use tauri:: { plugin:: PluginApi , AppHandle , Runtime } ;
3- use windows:: core:: HSTRING ;
4- use windows:: Foundation :: DateTime ;
5- use windows:: Services :: Store :: {
6- StoreContext , StoreLicense , StoreProduct , StorePurchaseProperties , StorePurchaseStatus ,
4+ use windows:: core:: { Interface , HSTRING } ;
5+ use windows:: {
6+ Foundation :: DateTime ,
7+ Services :: Store :: {
8+ StoreContext , StoreLicense , StoreProduct , StorePurchaseProperties , StorePurchaseStatus ,
9+ } ,
10+ Win32 :: UI :: Shell :: IInitializeWithWindow ,
711} ;
812use windows_collections:: IIterable ;
913
14+ use crate :: error:: { ErrorResponse , PluginInvokeError } ;
1015use crate :: models:: * ;
1116use std:: sync:: { Arc , RwLock } ;
1217
@@ -29,18 +34,52 @@ pub struct Iap<R: Runtime> {
2934impl < R : Runtime > Iap < R > {
3035 /// Get or create the StoreContext instance
3136 fn get_store_context ( & self ) -> crate :: Result < StoreContext > {
32- let mut context_guard = self . store_context . write ( ) . unwrap ( ) ;
37+ let mut context_guard = self . store_context . write ( ) . map_err ( |e| {
38+ crate :: Error :: PluginInvoke ( PluginInvokeError :: InvokeRejected ( ErrorResponse {
39+ code : Some ( "internalError" . to_string ( ) ) ,
40+ message : Some ( format ! ( "Failed to acquire write lock: {:?}" , e) ) ,
41+ data : ( ) ,
42+ } ) )
43+ } ) ?;
3344
3445 if context_guard. is_none ( ) {
3546 // Get the default store context for the current user
36- let context = StoreContext :: GetDefault ( ) . map_err ( |e| {
37- std:: io:: Error :: other ( format ! ( "Failed to get store context: {:?}" , e) )
47+ let context = StoreContext :: GetDefault ( ) ?;
48+
49+ let window = self . app_handle . get_webview_window ( "main" ) . ok_or_else ( || {
50+ crate :: Error :: PluginInvoke ( PluginInvokeError :: InvokeRejected ( ErrorResponse {
51+ code : Some ( "windowError" . to_string ( ) ) ,
52+ message : Some ( "Failed to get main window" . to_string ( ) ) ,
53+ data : ( ) ,
54+ } ) )
3855 } ) ?;
56+ let hwnd = window. hwnd ( ) . map_err ( |e| {
57+ crate :: Error :: PluginInvoke ( PluginInvokeError :: InvokeRejected ( ErrorResponse {
58+ code : Some ( "windowError" . to_string ( ) ) ,
59+ message : Some ( format ! ( "Failed to get window handle: {:?}" , e) ) ,
60+ data : ( ) ,
61+ } ) )
62+ } ) ?;
63+
64+ // Cast the WinRT object to IInitializeWithWindow and initialize it with your HWND
65+ let init = context. cast :: < IInitializeWithWindow > ( ) ?;
66+ unsafe {
67+ init. Initialize ( hwnd) ?;
68+ }
3969
4070 * context_guard = Some ( context) ;
4171 }
4272
43- Ok ( context_guard. as_ref ( ) . unwrap ( ) . clone ( ) )
73+ Ok ( context_guard
74+ . as_ref ( )
75+ . ok_or_else ( || {
76+ crate :: Error :: PluginInvoke ( PluginInvokeError :: InvokeRejected ( ErrorResponse {
77+ code : Some ( "storeNotInitialized" . to_string ( ) ) ,
78+ message : Some ( "Store context not initialized" . to_string ( ) ) ,
79+ data : ( ) ,
80+ } ) )
81+ } ) ?
82+ . clone ( ) )
4483 }
4584
4685 /// Convert Windows DateTime to Unix timestamp in milliseconds
@@ -79,9 +118,8 @@ impl<R: Runtime> Iap<R> {
79118 "inapp" => vec ! [
80119 HSTRING :: from( "Consumable" ) ,
81120 HSTRING :: from( "UnmanagedConsumable" ) ,
82- HSTRING :: from( "Durable" ) ,
83121 ] ,
84- "subs" => vec ! [ HSTRING :: from( "Subscription" ) ] ,
122+ "subs" => vec ! [ HSTRING :: from( "Subscription" ) , HSTRING :: from ( "Durable" ) ] ,
85123 _ => vec ! [
86124 HSTRING :: from( "Consumable" ) ,
87125 HSTRING :: from( "UnmanagedConsumable" ) ,
@@ -90,25 +128,27 @@ impl<R: Runtime> Iap<R> {
90128 ] ,
91129 } ;
92130
93- let kinds_it: IIterable < HSTRING > = IIterable :: try_from ( product_kinds)
94- . map_err ( |e| std:: io:: Error :: other ( format ! ( "Failed to create IIterable: {:?}" , e) ) ) ?;
95- let ids_it: IIterable < HSTRING > = IIterable :: try_from ( store_ids)
96- . map_err ( |e| std:: io:: Error :: other ( format ! ( "Failed to create IIterable: {:?}" , e) ) ) ?;
131+ let store_ids: IIterable < HSTRING > = store_ids. into ( ) ;
132+ let product_kinds: IIterable < HSTRING > = product_kinds. into ( ) ;
97133
98134 // Query products from the store
99135 let query_result = context
100- . GetStoreProductsAsync ( & kinds_it, & ids_it)
101- . and_then ( |async_op| async_op. get ( ) )
102- . map_err ( |e| std:: io:: Error :: other ( format ! ( "Failed to get products: {:?}" , e) ) ) ?;
136+ . GetStoreProductsAsync ( & product_kinds, & store_ids)
137+ . and_then ( |async_op| async_op. get ( ) ) ?;
103138
104139 // Check for any errors
105140 let extended_error = query_result. ExtendedError ( ) ?;
106141 if extended_error. is_err ( ) {
107- return Err ( std:: io:: Error :: other ( format ! (
108- "Store query failed with error: {:?}" ,
109- extended_error. message( )
110- ) )
111- . into ( ) ) ;
142+ return Err ( crate :: Error :: PluginInvoke (
143+ PluginInvokeError :: InvokeRejected ( ErrorResponse {
144+ code : Some ( "storeQueryFailed" . to_string ( ) ) ,
145+ message : Some ( format ! (
146+ "Store query failed with error: {:?}" ,
147+ extended_error. message( )
148+ ) ) ,
149+ data : ( ) ,
150+ } ) ,
151+ ) ) ;
112152 }
113153
114154 let products_map = query_result. Products ( ) ?;
@@ -248,9 +288,18 @@ impl<R: Runtime> Iap<R> {
248288 self . get_products ( vec ! [ product_id. clone( ) ] , product_type. clone ( ) ) ?;
249289
250290 if products_response. products . is_empty ( ) {
251- return Err ( std:: io:: Error :: other ( "Product not found" ) . into ( ) ) ;
291+ return Err ( crate :: Error :: PluginInvoke (
292+ PluginInvokeError :: InvokeRejected ( ErrorResponse {
293+ code : Some ( "productNotFound" . to_string ( ) ) ,
294+ message : Some ( "Product not found" . to_string ( ) ) ,
295+ data : ( ) ,
296+ } ) ,
297+ ) ) ;
252298 }
253299
300+ let product = & products_response. products [ 0 ] ;
301+ let product_title = product. title . clone ( ) ;
302+
254303 let store_id = HSTRING :: from ( & product_id) ;
255304
256305 // Create purchase properties if we have an offer token (for subscriptions)
@@ -264,14 +313,12 @@ impl<R: Runtime> Iap<R> {
264313
265314 context
266315 . RequestPurchaseWithPurchasePropertiesAsync ( & store_id, & properties)
267- . and_then ( |async_op| async_op. get ( ) )
268- . map_err ( |e| std:: io:: Error :: other ( format ! ( "Purchase request failed: {:?}" , e) ) ) ?
316+ . and_then ( |async_op| async_op. get ( ) ) ?
269317 } else {
270318 // Simple purchase without properties
271319 context
272320 . RequestPurchaseAsync ( & store_id)
273- . and_then ( |async_op| async_op. get ( ) )
274- . map_err ( |e| std:: io:: Error :: other ( format ! ( "Purchase request failed: {:?}" , e) ) ) ?
321+ . and_then ( |async_op| async_op. get ( ) ) ?
275322 } ;
276323
277324 // Check purchase status
@@ -280,14 +327,42 @@ impl<R: Runtime> Iap<R> {
280327 let purchase_state = match status {
281328 StorePurchaseStatus :: Succeeded => PurchaseStateValue :: Purchased as i32 ,
282329 StorePurchaseStatus :: AlreadyPurchased => PurchaseStateValue :: Purchased as i32 ,
283- StorePurchaseStatus :: NotPurchased => PurchaseStateValue :: Canceled as i32 ,
330+ StorePurchaseStatus :: NotPurchased => {
331+ return Err ( crate :: Error :: PluginInvoke (
332+ PluginInvokeError :: InvokeRejected ( ErrorResponse {
333+ code : Some ( "purchaseNotCompleted" . to_string ( ) ) ,
334+ message : Some ( "Purchase was not completed" . to_string ( ) ) ,
335+ data : ( ) ,
336+ } ) ,
337+ ) ) ;
338+ }
284339 StorePurchaseStatus :: NetworkError => {
285- return Err ( std:: io:: Error :: other ( "Network error during purchase" ) . into ( ) ) ;
340+ return Err ( crate :: Error :: PluginInvoke (
341+ PluginInvokeError :: InvokeRejected ( ErrorResponse {
342+ code : Some ( "networkError" . to_string ( ) ) ,
343+ message : Some ( "Network error during purchase" . to_string ( ) ) ,
344+ data : ( ) ,
345+ } ) ,
346+ ) ) ;
286347 }
287348 StorePurchaseStatus :: ServerError => {
288- return Err ( std:: io:: Error :: other ( "Server error during purchase" ) . into ( ) ) ;
349+ return Err ( crate :: Error :: PluginInvoke (
350+ PluginInvokeError :: InvokeRejected ( ErrorResponse {
351+ code : Some ( "serverError" . to_string ( ) ) ,
352+ message : Some ( "Server error during purchase" . to_string ( ) ) ,
353+ data : ( ) ,
354+ } ) ,
355+ ) ) ;
356+ }
357+ _ => {
358+ return Err ( crate :: Error :: PluginInvoke (
359+ PluginInvokeError :: InvokeRejected ( ErrorResponse {
360+ code : Some ( "purchaseFailed" . to_string ( ) ) ,
361+ message : Some ( "Purchase failed" . to_string ( ) ) ,
362+ data : ( ) ,
363+ } ) ,
364+ ) ) ;
289365 }
290- _ => return Err ( std:: io:: Error :: other ( "Purchase failed" ) . into ( ) ) ,
291366 } ;
292367
293368 // Get extended error info if available
@@ -301,14 +376,20 @@ impl<R: Runtime> Iap<R> {
301376 // Generate purchase details
302377 let purchase_time = std:: time:: SystemTime :: now ( )
303378 . duration_since ( std:: time:: UNIX_EPOCH )
304- . unwrap ( )
379+ . map_err ( |e| {
380+ crate :: Error :: PluginInvoke ( PluginInvokeError :: InvokeRejected ( ErrorResponse {
381+ code : Some ( "systemTimeError" . to_string ( ) ) ,
382+ message : Some ( format ! ( "Failed to get system time: {:?}" , e) ) ,
383+ data : ( ) ,
384+ } ) )
385+ } ) ?
305386 . as_millis ( ) as i64 ;
306387
307388 let purchase_token = format ! ( "win_{}_{}" , product_id, purchase_time) ;
308389
309390 Ok ( Purchase {
310391 order_id : Some ( purchase_token. clone ( ) ) ,
311- package_name : self . app_handle . package_info ( ) . name . clone ( ) ,
392+ package_name : product_title ,
312393 product_id : product_id. clone ( ) ,
313394 purchase_time,
314395 purchase_token : purchase_token. clone ( ) ,
@@ -332,8 +413,7 @@ impl<R: Runtime> Iap<R> {
332413 // Get app license info
333414 let app_license = context
334415 . GetAppLicenseAsync ( )
335- . and_then ( |async_op| async_op. get ( ) )
336- . map_err ( |e| std:: io:: Error :: other ( format ! ( "Failed to get app license: {:?}" , e) ) ) ?;
416+ . and_then ( |async_op| async_op. get ( ) ) ?;
337417
338418 let mut purchases = Vec :: new ( ) ;
339419
@@ -377,7 +457,13 @@ impl<R: Runtime> Iap<R> {
377457 } else {
378458 std:: time:: SystemTime :: now ( )
379459 . duration_since ( std:: time:: UNIX_EPOCH )
380- . unwrap ( )
460+ . map_err ( |e| {
461+ crate :: Error :: PluginInvoke ( PluginInvokeError :: InvokeRejected ( ErrorResponse {
462+ code : Some ( "systemTimeError" . to_string ( ) ) ,
463+ message : Some ( format ! ( "Failed to get system time: {:?}" , e) ) ,
464+ data : ( ) ,
465+ } ) )
466+ } ) ?
381467 . as_millis ( ) as i64
382468 } ;
383469
@@ -423,8 +509,7 @@ impl<R: Runtime> Iap<R> {
423509 // Get app license to check ownership
424510 let app_license = context
425511 . GetAppLicenseAsync ( )
426- . and_then ( |async_op| async_op. get ( ) )
427- . map_err ( |e| std:: io:: Error :: other ( format ! ( "Failed to get app license: {:?}" , e) ) ) ?;
512+ . and_then ( |async_op| async_op. get ( ) ) ?;
428513
429514 let addon_licenses = app_license. AddOnLicenses ( ) ?;
430515
0 commit comments