Skip to main content

storekit/
purchase_option.rs

1use serde::{Deserialize, Serialize};
2
3use crate::error::StoreKitError;
4use crate::transaction::{Transaction, TransactionPayload};
5use crate::verification_result::{VerificationResult, VerificationResultPayload};
6
7#[derive(Debug, Clone, PartialEq, Serialize)]
8#[serde(tag = "kind", rename_all = "camelCase")]
9pub enum PurchaseOption {
10    AppAccountToken {
11        app_account_token: String,
12    },
13    Quantity {
14        quantity: i64,
15    },
16    SimulatesAskToBuyInSandbox {
17        simulate_ask_to_buy_in_sandbox: bool,
18    },
19    CustomString {
20        key: String,
21        value: String,
22    },
23    CustomNumber {
24        key: String,
25        value: f64,
26    },
27    CustomBool {
28        key: String,
29        value: bool,
30    },
31    CustomData {
32        key: String,
33        value_base64: String,
34    },
35    PromotionalOfferSignature {
36        offer_id: String,
37        key_id: String,
38        nonce: String,
39        signature_base64: String,
40        timestamp: i64,
41    },
42    PromotionalOfferCompactJws {
43        offer_id: String,
44        compact_jws: String,
45    },
46    IntroductoryOfferEligibility {
47        compact_jws: String,
48    },
49    WinBackOffer {
50        offer_id: String,
51    },
52    OnStorefrontChange {
53        should_continue_purchase: bool,
54    },
55}
56
57#[allow(clippy::large_enum_variant)]
58#[derive(Debug)]
59pub enum PurchaseResult {
60    Success(VerificationResult<Transaction>),
61    UserCancelled,
62    Pending,
63}
64
65#[allow(clippy::unsafe_derive_deserialize)]
66#[derive(Debug, Deserialize)]
67pub(crate) struct PurchaseResultPayload {
68    kind: String,
69    #[serde(rename = "verificationResult")]
70    verification_result: Option<VerificationResultPayload<TransactionPayload>>,
71}
72
73impl PurchaseResultPayload {
74    pub(crate) fn into_purchase_result(
75        self,
76        transaction_handle: *mut core::ffi::c_void,
77    ) -> Result<PurchaseResult, StoreKitError> {
78        match self.kind.as_str() {
79            "success" => {
80                let verification_result = self.verification_result.ok_or_else(|| {
81                    StoreKitError::Unknown(
82                        "StoreKit reported a successful purchase without a verification result"
83                            .to_owned(),
84                    )
85                })?;
86                let transaction = verification_result.into_result(|payload| {
87                    Transaction::from_raw_parts(transaction_handle, payload)
88                })?;
89                Ok(PurchaseResult::Success(transaction))
90            }
91            "userCancelled" => {
92                if !transaction_handle.is_null() {
93                    unsafe { crate::ffi::sk_transaction_release(transaction_handle) };
94                }
95                Ok(PurchaseResult::UserCancelled)
96            }
97            "pending" => {
98                if !transaction_handle.is_null() {
99                    unsafe { crate::ffi::sk_transaction_release(transaction_handle) };
100                }
101                Ok(PurchaseResult::Pending)
102            }
103            other => {
104                if !transaction_handle.is_null() {
105                    unsafe { crate::ffi::sk_transaction_release(transaction_handle) };
106                }
107                Err(StoreKitError::Unknown(format!(
108                    "StoreKit returned an unknown purchase result kind '{other}'"
109                )))
110            }
111        }
112    }
113}