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