Skip to main content

storekit/
store_context.rs

1use serde::Deserialize;
2
3use crate::app_store::AppStore;
4use crate::app_transaction::AppTransaction;
5use crate::error::StoreKitError;
6use crate::ffi;
7use crate::product::Product;
8use crate::receipt_validator::{AppReceipt, ReceiptValidator};
9use crate::storefront::Storefront;
10use crate::transaction::{Transaction, TransactionStream};
11use crate::verification_result::VerificationResult;
12
13#[derive(Debug, Clone, PartialEq, Eq)]
14/// Collects bundle context used alongside `StoreKit` APIs.
15pub struct StoreContext {
16    /// Bundle identifier reported by `StoreKit`.
17    pub bundle_identifier: Option<String>,
18    /// Bundle display name reported by `StoreKit`.
19    pub bundle_name: Option<String>,
20    /// Bundle version reported by `StoreKit`.
21    pub bundle_version: Option<String>,
22    /// Receipt URL reported by `StoreKit`.
23    pub receipt_url: Option<String>,
24    /// Whether `StoreKit` reports that payments can be made.
25    pub can_make_payments: bool,
26    /// Device verification identifier reported by `StoreKit`.
27    pub device_verification_id: Option<String>,
28    /// Whether `StoreKit` reported that the app is bundled.
29    pub is_bundled: bool,
30    /// Executable path reported by `StoreKit`.
31    pub executable_path: Option<String>,
32}
33
34impl StoreContext {
35    /// Fetches the current bundle context used alongside `StoreKit` APIs.
36    pub fn current() -> Result<Self, StoreKitError> {
37        let mut context_json = core::ptr::null_mut();
38        let mut error_message = core::ptr::null_mut();
39        let status = unsafe { ffi::sk_store_context_json(&mut context_json, &mut error_message) };
40        if status != ffi::status::OK {
41            return Err(unsafe { crate::private::error_from_status(status, error_message) });
42        }
43        let payload = unsafe {
44            crate::private::parse_json_ptr::<StoreContextPayload>(context_json, "store context")
45        }?;
46        Ok(payload.into_store_context())
47    }
48
49    /// Returns whether `StoreKit` reports that payments can be made.
50    pub fn can_make_payments() -> Result<bool, StoreKitError> {
51        AppStore::can_make_payments()
52    }
53
54    /// Fetches the `StoreKit` device verification identifier.
55    pub fn device_verification_id() -> Result<Option<String>, StoreKitError> {
56        AppStore::device_verification_id()
57    }
58
59    /// Fetches current `StoreKit` products for the supplied identifiers.
60    pub fn current_products<I, S>(identifiers: I) -> Result<Vec<Product>, StoreKitError>
61    where
62        I: IntoIterator<Item = S>,
63        S: AsRef<str>,
64    {
65        Product::products_for(identifiers)
66    }
67
68    /// Creates a stream of current `StoreKit` entitlements.
69    pub fn current_entitlements() -> Result<TransactionStream, StoreKitError> {
70        Transaction::current_entitlements()
71    }
72
73    /// Creates a stream of `StoreKit` transaction updates.
74    pub fn transaction_updates() -> Result<TransactionStream, StoreKitError> {
75        Transaction::updates()
76    }
77
78    /// Fetches the current `StoreKit` storefront.
79    pub fn current_storefront() -> Result<Option<Storefront>, StoreKitError> {
80        Storefront::current()
81    }
82
83    /// Fetches the shared `StoreKit` app transaction.
84    pub fn app_transaction() -> Result<VerificationResult<AppTransaction>, StoreKitError> {
85        AppTransaction::shared()
86    }
87
88    /// Fetches the current app receipt used by `StoreKit`.
89    pub fn receipt() -> Result<Option<AppReceipt>, StoreKitError> {
90        ReceiptValidator::current_receipt()
91    }
92}
93
94#[derive(Debug, Deserialize)]
95struct StoreContextPayload {
96    #[serde(rename = "bundleIdentifier")]
97    bundle_identifier: Option<String>,
98    #[serde(rename = "bundleName")]
99    bundle_name: Option<String>,
100    #[serde(rename = "bundleVersion")]
101    bundle_version: Option<String>,
102    #[serde(rename = "receiptURL")]
103    receipt_url: Option<String>,
104    #[serde(rename = "canMakePayments")]
105    can_make_payments: bool,
106    #[serde(rename = "deviceVerificationID")]
107    device_verification_id: Option<String>,
108    #[serde(rename = "isBundled")]
109    is_bundled: bool,
110    #[serde(rename = "executablePath")]
111    executable_path: Option<String>,
112}
113
114impl StoreContextPayload {
115    fn into_store_context(self) -> StoreContext {
116        StoreContext {
117            bundle_identifier: self.bundle_identifier,
118            bundle_name: self.bundle_name,
119            bundle_version: self.bundle_version,
120            receipt_url: self.receipt_url,
121            can_make_payments: self.can_make_payments,
122            device_verification_id: self.device_verification_id,
123            is_bundled: self.is_bundled,
124            executable_path: self.executable_path,
125        }
126    }
127}