storekit-rs 0.2.0

Safe Rust bindings for Apple's StoreKit framework — in-app purchases and transaction streams on macOS
Documentation
use serde::Deserialize;

use crate::app_store::AppStore;
use crate::app_transaction::AppTransaction;
use crate::error::StoreKitError;
use crate::ffi;
use crate::product::Product;
use crate::receipt_validator::{AppReceipt, ReceiptValidator};
use crate::storefront::Storefront;
use crate::transaction::{Transaction, TransactionStream};
use crate::verification_result::VerificationResult;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StoreContext {
    pub bundle_identifier: Option<String>,
    pub bundle_name: Option<String>,
    pub bundle_version: Option<String>,
    pub receipt_url: Option<String>,
    pub can_make_payments: bool,
    pub device_verification_id: Option<String>,
    pub is_bundled: bool,
    pub executable_path: Option<String>,
}

impl StoreContext {
    pub fn current() -> Result<Self, StoreKitError> {
        let mut context_json = core::ptr::null_mut();
        let mut error_message = core::ptr::null_mut();
        let status = unsafe { ffi::sk_store_context_json(&mut context_json, &mut error_message) };
        if status != ffi::status::OK {
            return Err(unsafe { crate::private::error_from_status(status, error_message) });
        }
        let payload = unsafe {
            crate::private::parse_json_ptr::<StoreContextPayload>(context_json, "store context")
        }?;
        Ok(payload.into_store_context())
    }

    pub fn can_make_payments() -> Result<bool, StoreKitError> {
        AppStore::can_make_payments()
    }

    pub fn device_verification_id() -> Result<Option<String>, StoreKitError> {
        AppStore::device_verification_id()
    }

    pub fn current_products<I, S>(identifiers: I) -> Result<Vec<Product>, StoreKitError>
    where
        I: IntoIterator<Item = S>,
        S: AsRef<str>,
    {
        Product::products_for(identifiers)
    }

    pub fn current_entitlements() -> Result<TransactionStream, StoreKitError> {
        Transaction::current_entitlements()
    }

    pub fn transaction_updates() -> Result<TransactionStream, StoreKitError> {
        Transaction::updates()
    }

    pub fn current_storefront() -> Result<Option<Storefront>, StoreKitError> {
        Storefront::current()
    }

    pub fn app_transaction() -> Result<VerificationResult<AppTransaction>, StoreKitError> {
        AppTransaction::shared()
    }

    pub fn receipt() -> Result<Option<AppReceipt>, StoreKitError> {
        ReceiptValidator::current_receipt()
    }
}

#[derive(Debug, Deserialize)]
struct StoreContextPayload {
    #[serde(rename = "bundleIdentifier")]
    bundle_identifier: Option<String>,
    #[serde(rename = "bundleName")]
    bundle_name: Option<String>,
    #[serde(rename = "bundleVersion")]
    bundle_version: Option<String>,
    #[serde(rename = "receiptURL")]
    receipt_url: Option<String>,
    #[serde(rename = "canMakePayments")]
    can_make_payments: bool,
    #[serde(rename = "deviceVerificationID")]
    device_verification_id: Option<String>,
    #[serde(rename = "isBundled")]
    is_bundled: bool,
    #[serde(rename = "executablePath")]
    executable_path: Option<String>,
}

impl StoreContextPayload {
    fn into_store_context(self) -> StoreContext {
        StoreContext {
            bundle_identifier: self.bundle_identifier,
            bundle_name: self.bundle_name,
            bundle_version: self.bundle_version,
            receipt_url: self.receipt_url,
            can_make_payments: self.can_make_payments,
            device_verification_id: self.device_verification_id,
            is_bundled: self.is_bundled,
            executable_path: self.executable_path,
        }
    }
}