storekit/
subscription_info.rs1use 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};
10use crate::renewal_info::{RenewalInfo, RenewalInfoPayload};
11use crate::renewal_state::RenewalState;
12use crate::subscription::{
13 SubscriptionOffer, SubscriptionOfferPayload, SubscriptionPeriod, SubscriptionPeriodPayload,
14};
15use crate::transaction::{Transaction, TransactionPayload};
16use crate::verification_result::{VerificationResult, VerificationResultPayload};
17
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct SubscriptionInfo {
20 pub introductory_offer: Option<SubscriptionOffer>,
21 pub promotional_offers: Vec<SubscriptionOffer>,
22 pub win_back_offers: Vec<SubscriptionOffer>,
23 pub subscription_group_id: String,
24 pub subscription_period: SubscriptionPeriod,
25 pub group_level: Option<i64>,
26 pub group_display_name: Option<String>,
27}
28
29impl SubscriptionInfo {
30 pub fn is_eligible_for_intro_offer(&self) -> Result<bool, StoreKitError> {
31 Self::is_eligible_for_intro_offer_for(&self.subscription_group_id)
32 }
33
34 pub fn is_eligible_for_intro_offer_for(group_id: &str) -> Result<bool, StoreKitError> {
35 let group_id = cstring_from_str(group_id, "subscription group id")?;
36 let mut raw_value = 0;
37 let mut error_message = ptr::null_mut();
38 let status = unsafe {
39 ffi::sk_subscription_info_is_eligible_for_intro_offer(
40 group_id.as_ptr(),
41 &mut raw_value,
42 &mut error_message,
43 )
44 };
45 if status == ffi::status::OK {
46 Ok(raw_value != 0)
47 } else {
48 Err(unsafe { error_from_status(status, error_message) })
49 }
50 }
51
52 pub fn status(&self) -> Result<Vec<SubscriptionStatus>, StoreKitError> {
53 Self::status_for(&self.subscription_group_id)
54 }
55
56 pub fn status_for(group_id: &str) -> Result<Vec<SubscriptionStatus>, StoreKitError> {
57 let group_id = cstring_from_str(group_id, "subscription group id")?;
58 let mut statuses_json = ptr::null_mut();
59 let mut error_message = ptr::null_mut();
60 let status = unsafe {
61 ffi::sk_subscription_info_statuses_json(
62 group_id.as_ptr(),
63 &mut statuses_json,
64 &mut error_message,
65 )
66 };
67 if status != ffi::status::OK {
68 return Err(unsafe { error_from_status(status, error_message) });
69 }
70 let payloads = unsafe {
71 parse_json_ptr::<Vec<SubscriptionStatusPayload>>(statuses_json, "subscription statuses")
72 }?;
73 payloads
74 .into_iter()
75 .map(SubscriptionStatusPayload::into_subscription_status)
76 .collect::<Result<Vec<_>, _>>()
77 }
78
79 pub fn status_for_transaction(
80 transaction_id: u64,
81 ) -> Result<Option<SubscriptionStatus>, StoreKitError> {
82 let transaction_id = cstring_from_str(&transaction_id.to_string(), "transaction id")?;
83 let mut status_json = ptr::null_mut();
84 let mut error_message = ptr::null_mut();
85 let status = unsafe {
86 ffi::sk_subscription_info_status_for_transaction(
87 transaction_id.as_ptr(),
88 &mut status_json,
89 &mut error_message,
90 )
91 };
92 if status != ffi::status::OK {
93 return Err(unsafe { error_from_status(status, error_message) });
94 }
95 unsafe {
96 parse_optional_json_ptr::<SubscriptionStatusPayload>(
97 status_json,
98 "subscription status for transaction",
99 )
100 }
101 .and_then(|payload| {
102 payload
103 .map(SubscriptionStatusPayload::into_subscription_status)
104 .transpose()
105 })
106 }
107}
108
109#[derive(Debug, Clone)]
110pub struct SubscriptionStatus {
111 pub state: RenewalState,
112 pub transaction: VerificationResult<Transaction>,
113 pub renewal_info: VerificationResult<RenewalInfo>,
114}
115
116#[derive(Debug, Deserialize)]
117pub(crate) struct SubscriptionInfoPayload {
118 #[serde(rename = "introductoryOffer")]
119 introductory_offer: Option<SubscriptionOfferPayload>,
120 #[serde(rename = "promotionalOffers")]
121 promotional_offers: Vec<SubscriptionOfferPayload>,
122 #[serde(rename = "winBackOffers")]
123 win_back_offers: Vec<SubscriptionOfferPayload>,
124 #[serde(rename = "subscriptionGroupID")]
125 subscription_group_id: String,
126 #[serde(rename = "subscriptionPeriod")]
127 subscription_period: SubscriptionPeriodPayload,
128 #[serde(rename = "groupLevel")]
129 group_level: Option<i64>,
130 #[serde(rename = "groupDisplayName")]
131 group_display_name: Option<String>,
132}
133
134impl SubscriptionInfoPayload {
135 pub(crate) fn into_subscription_info(self) -> SubscriptionInfo {
136 SubscriptionInfo {
137 introductory_offer: self
138 .introductory_offer
139 .map(SubscriptionOfferPayload::into_subscription_offer),
140 promotional_offers: self
141 .promotional_offers
142 .into_iter()
143 .map(SubscriptionOfferPayload::into_subscription_offer)
144 .collect(),
145 win_back_offers: self
146 .win_back_offers
147 .into_iter()
148 .map(SubscriptionOfferPayload::into_subscription_offer)
149 .collect(),
150 subscription_group_id: self.subscription_group_id,
151 subscription_period: self.subscription_period.into_subscription_period(),
152 group_level: self.group_level,
153 group_display_name: self.group_display_name,
154 }
155 }
156}
157
158#[derive(Debug, Deserialize)]
159pub(crate) struct SubscriptionStatusPayload {
160 state: String,
161 transaction: VerificationResultPayload<TransactionPayload>,
162 #[serde(rename = "renewalInfo")]
163 renewal_info: VerificationResultPayload<RenewalInfoPayload>,
164}
165
166impl SubscriptionStatusPayload {
167 pub(crate) fn into_subscription_status(self) -> Result<SubscriptionStatus, StoreKitError> {
168 Ok(SubscriptionStatus {
169 state: RenewalState::from_raw(self.state),
170 transaction: self
171 .transaction
172 .into_result(Transaction::from_snapshot_payload)?,
173 renewal_info: self
174 .renewal_info
175 .into_result(|payload| Ok(payload.into_renewal_info()))?,
176 })
177 }
178}