Skip to main content

storekit/
external_purchase.rs

1use core::ptr;
2
3use serde::Deserialize;
4
5use crate::error::StoreKitError;
6use crate::ffi;
7use crate::private::{
8    cstring_from_str, error_from_status, parse_json_ptr, parse_optional_json_ptr,
9};
10
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum ExternalPurchaseNoticeResult {
13    Continued,
14    Cancelled,
15    ContinuedWithExternalPurchaseToken { token: String },
16    Unknown(String),
17}
18
19impl ExternalPurchaseNoticeResult {
20    pub fn as_str(&self) -> &str {
21        match self {
22            Self::Continued => "continued",
23            Self::Cancelled => "cancelled",
24            Self::ContinuedWithExternalPurchaseToken { .. } => "continuedWithExternalPurchaseToken",
25            Self::Unknown(value) => value.as_str(),
26        }
27    }
28}
29
30#[derive(Debug, Clone, Copy, Default)]
31pub struct ExternalPurchase;
32
33impl ExternalPurchase {
34    pub fn can_present() -> Result<bool, StoreKitError> {
35        let mut raw_value = 0;
36        let mut error_message = ptr::null_mut();
37        let status = unsafe {
38            ffi::sk_external_purchase_can_present(&mut raw_value, &mut error_message)
39        };
40        if status == ffi::status::OK {
41            Ok(raw_value != 0)
42        } else {
43            Err(unsafe { error_from_status(status, error_message) })
44        }
45    }
46
47    pub fn present_notice_sheet() -> Result<ExternalPurchaseNoticeResult, StoreKitError> {
48        let mut result_json = ptr::null_mut();
49        let mut error_message = ptr::null_mut();
50        let status = unsafe {
51            ffi::sk_external_purchase_present_notice_result_json(&mut result_json, &mut error_message)
52        };
53        if status != ffi::status::OK {
54            return Err(unsafe { error_from_status(status, error_message) });
55        }
56        let payload = unsafe {
57            parse_json_ptr::<ExternalPurchaseNoticeResultPayload>(
58                result_json,
59                "external purchase notice result",
60            )
61        }?;
62        Ok(payload.into_notice_result())
63    }
64}
65
66#[derive(Debug, Clone, Copy, Default)]
67pub struct ExternalPurchaseLink;
68
69impl ExternalPurchaseLink {
70    pub fn can_open() -> Result<bool, StoreKitError> {
71        let mut raw_value = 0;
72        let mut error_message = ptr::null_mut();
73        let status = unsafe { ffi::sk_external_purchase_link_can_open(&mut raw_value, &mut error_message) };
74        if status == ffi::status::OK {
75            Ok(raw_value != 0)
76        } else {
77            Err(unsafe { error_from_status(status, error_message) })
78        }
79    }
80
81    pub fn eligible_urls() -> Result<Option<Vec<String>>, StoreKitError> {
82        let mut urls_json = ptr::null_mut();
83        let mut error_message = ptr::null_mut();
84        let status = unsafe {
85            ffi::sk_external_purchase_link_eligible_urls_json(&mut urls_json, &mut error_message)
86        };
87        if status != ffi::status::OK {
88            return Err(unsafe { error_from_status(status, error_message) });
89        }
90        unsafe { parse_optional_json_ptr::<Vec<String>>(urls_json, "eligible external purchase URLs") }
91    }
92
93    pub fn open() -> Result<(), StoreKitError> {
94        let mut error_message = ptr::null_mut();
95        let status = unsafe { ffi::sk_external_purchase_link_open(&mut error_message) };
96        if status == ffi::status::OK {
97            Ok(())
98        } else {
99            Err(unsafe { error_from_status(status, error_message) })
100        }
101    }
102
103    pub fn open_url(url: &str) -> Result<(), StoreKitError> {
104        let url = cstring_from_str(url, "external purchase URL")?;
105        let mut error_message = ptr::null_mut();
106        let status = unsafe {
107            ffi::sk_external_purchase_link_open_url(url.as_ptr(), &mut error_message)
108        };
109        if status == ffi::status::OK {
110            Ok(())
111        } else {
112            Err(unsafe { error_from_status(status, error_message) })
113        }
114    }
115}
116
117#[derive(Debug, Clone, Copy, PartialEq, Eq)]
118pub enum ExternalPurchaseCustomLinkNoticeType {
119    WithinApp,
120    Browser,
121    Unknown(i64),
122}
123
124impl ExternalPurchaseCustomLinkNoticeType {
125    pub const fn as_raw(&self) -> i64 {
126        match self {
127            Self::WithinApp => 0,
128            Self::Browser => 1,
129            Self::Unknown(value) => *value,
130        }
131    }
132
133}
134
135#[derive(Debug, Clone, PartialEq, Eq)]
136pub enum ExternalPurchaseCustomLinkNoticeResult {
137    Cancelled,
138    Continued,
139    Unknown(String),
140}
141
142impl ExternalPurchaseCustomLinkNoticeResult {
143    pub fn as_str(&self) -> &str {
144        match self {
145            Self::Cancelled => "cancelled",
146            Self::Continued => "continued",
147            Self::Unknown(value) => value.as_str(),
148        }
149    }
150}
151
152#[derive(Debug, Clone, PartialEq, Eq)]
153pub struct ExternalPurchaseCustomLinkToken {
154    pub value: String,
155}
156
157#[derive(Debug, Clone, Copy, Default)]
158pub struct ExternalPurchaseCustomLink;
159
160impl ExternalPurchaseCustomLink {
161    pub fn is_eligible() -> Result<bool, StoreKitError> {
162        let mut raw_value = 0;
163        let mut error_message = ptr::null_mut();
164        let status = unsafe {
165            ffi::sk_external_purchase_custom_link_is_eligible(&mut raw_value, &mut error_message)
166        };
167        if status == ffi::status::OK {
168            Ok(raw_value != 0)
169        } else {
170            Err(unsafe { error_from_status(status, error_message) })
171        }
172    }
173
174    pub fn show_notice(
175        notice_type: ExternalPurchaseCustomLinkNoticeType,
176    ) -> Result<ExternalPurchaseCustomLinkNoticeResult, StoreKitError> {
177        let raw_notice_type = i32::try_from(notice_type.as_raw()).map_err(|_| {
178            StoreKitError::InvalidArgument(format!(
179                "external purchase custom link notice type '{}' does not fit in i32",
180                notice_type.as_raw()
181            ))
182        })?;
183        let mut result_json = ptr::null_mut();
184        let mut error_message = ptr::null_mut();
185        let status = unsafe {
186            ffi::sk_external_purchase_custom_link_show_notice_result_json(
187                raw_notice_type,
188                &mut result_json,
189                &mut error_message,
190            )
191        };
192        if status != ffi::status::OK {
193            return Err(unsafe { error_from_status(status, error_message) });
194        }
195        let payload = unsafe {
196            parse_json_ptr::<ExternalPurchaseCustomLinkNoticeResultPayload>(
197                result_json,
198                "external purchase custom link notice result",
199            )
200        }?;
201        Ok(payload.into_notice_result())
202    }
203
204    pub fn token(token_type: &str) -> Result<Option<ExternalPurchaseCustomLinkToken>, StoreKitError> {
205        let token_type = cstring_from_str(token_type, "external purchase custom link token type")?;
206        let mut token_json = ptr::null_mut();
207        let mut error_message = ptr::null_mut();
208        let status = unsafe {
209            ffi::sk_external_purchase_custom_link_token_json(
210                token_type.as_ptr(),
211                &mut token_json,
212                &mut error_message,
213            )
214        };
215        if status != ffi::status::OK {
216            return Err(unsafe { error_from_status(status, error_message) });
217        }
218        unsafe {
219            parse_optional_json_ptr::<ExternalPurchaseCustomLinkTokenPayload>(
220                token_json,
221                "external purchase custom link token",
222            )
223        }
224        .map(|payload| {
225            payload.map(|payload| ExternalPurchaseCustomLinkToken { value: payload.value })
226        })
227    }
228}
229
230#[derive(Debug, Deserialize)]
231struct ExternalPurchaseNoticeResultPayload {
232    kind: String,
233    token: Option<String>,
234}
235
236impl ExternalPurchaseNoticeResultPayload {
237    fn into_notice_result(self) -> ExternalPurchaseNoticeResult {
238        match self.kind.as_str() {
239            "continued" => ExternalPurchaseNoticeResult::Continued,
240            "cancelled" => ExternalPurchaseNoticeResult::Cancelled,
241            "continuedWithExternalPurchaseToken" => {
242                ExternalPurchaseNoticeResult::ContinuedWithExternalPurchaseToken {
243                    token: self.token.unwrap_or_default(),
244                }
245            }
246            other => ExternalPurchaseNoticeResult::Unknown(other.to_owned()),
247        }
248    }
249}
250
251#[derive(Debug, Deserialize)]
252struct ExternalPurchaseCustomLinkNoticeResultPayload {
253    kind: String,
254}
255
256impl ExternalPurchaseCustomLinkNoticeResultPayload {
257    fn into_notice_result(self) -> ExternalPurchaseCustomLinkNoticeResult {
258        match self.kind.as_str() {
259            "cancelled" => ExternalPurchaseCustomLinkNoticeResult::Cancelled,
260            "continued" => ExternalPurchaseCustomLinkNoticeResult::Continued,
261            other => ExternalPurchaseCustomLinkNoticeResult::Unknown(other.to_owned()),
262        }
263    }
264}
265
266#[derive(Debug, Deserialize)]
267struct ExternalPurchaseCustomLinkTokenPayload {
268    value: String,
269}