Skip to content

Commit c2ca491

Browse files
committed
Cleanup
1 parent 25add61 commit c2ca491

File tree

2 files changed

+124
-41
lines changed

2 files changed

+124
-41
lines changed

Cargo.toml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ repository = "https://github.com/Choochmeque/tauri-plugin-iap"
1414
default = []
1515
unstable = [
1616
"dep:swift-bridge",
17-
"dep:serde_json",
1817
"dep:objc2",
1918
"dep:objc2-core-foundation",
2019
"dep:objc2-security",
@@ -23,11 +22,11 @@ unstable = [
2322
[dependencies]
2423
tauri = { version = "2.7.0" }
2524
serde = "1.0"
25+
serde_json = "1.0"
2626
thiserror = "2"
2727

2828
[target.'cfg(target_os = "macos")'.dependencies]
2929
swift-bridge = { version = "0.1", features = ["async"], optional = true }
30-
serde_json = { version = "1.0", optional = true }
3130
objc2 = { version = "0.6", optional = true }
3231
objc2-core-foundation = { version = "0.3", optional = true }
3332
objc2-security = { version = "0.3", optional = true }
@@ -41,7 +40,6 @@ windows = { version = "0.61", features = [
4140
] }
4241
windows-result = "0.3"
4342
windows-collections = "0.2"
44-
serde_json = "1.0"
4543

4644
[build-dependencies]
4745
tauri-plugin = { version = "2.3.0", features = ["build"] }

src/windows.rs

Lines changed: 123 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
use serde::de::DeserializeOwned;
2+
use tauri::Manager;
23
use 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
};
812
use windows_collections::IIterable;
913

14+
use crate::error::{ErrorResponse, PluginInvokeError};
1015
use crate::models::*;
1116
use std::sync::{Arc, RwLock};
1217

@@ -29,18 +34,52 @@ pub struct Iap<R: Runtime> {
2934
impl<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

Comments
 (0)