Skip to main content

storekit/
verification_result.rs

1use serde::Deserialize;
2
3use crate::error::{StoreKitError, VerificationErrorPayload, VerificationFailure};
4use crate::private::decode_base64;
5
6#[derive(Debug, Clone, PartialEq, Eq)]
7/// Carries metadata returned alongside `StoreKit.VerificationResult`.
8pub struct VerificationMetadata {
9    /// `StoreKit`-provided `jws_representation` value.
10    pub jws_representation: String,
11    /// Decoded JWS header bytes returned by `StoreKit`.
12    pub header_data: Vec<u8>,
13    /// Decoded JWS payload bytes returned by `StoreKit`.
14    pub payload_data: Vec<u8>,
15    /// Decoded JWS signature bytes returned by `StoreKit`.
16    pub signature_data: Vec<u8>,
17    /// Decoded signed-data bytes returned by `StoreKit`.
18    pub signed_data: Vec<u8>,
19    /// Signature timestamp reported by `StoreKit`.
20    pub signed_date: String,
21    /// Decoded device-verification bytes returned by `StoreKit`.
22    pub device_verification: Vec<u8>,
23    /// Device verification nonce reported by `StoreKit`.
24    pub device_verification_nonce: String,
25}
26
27impl VerificationMetadata {
28    /// Returns the compact JWS representation returned by `StoreKit`.
29    pub fn jws_representation(&self) -> &str {
30        &self.jws_representation
31    }
32}
33
34#[derive(Debug, Clone, PartialEq, Eq)]
35/// Wraps `StoreKit.VerificationResult`.
36pub enum VerificationResult<T> {
37    /// `StoreKit` verified the payload signature.
38    Verified {
39        /// Decoded payload returned by `StoreKit`.
40        payload: T,
41        /// Verification metadata returned by `StoreKit`.
42        metadata: VerificationMetadata,
43    },
44    /// `StoreKit` returned the payload but verification failed.
45    Unverified {
46        /// Decoded payload returned by `StoreKit`.
47        payload: T,
48        /// Verification metadata returned by `StoreKit`.
49        metadata: VerificationMetadata,
50        /// Verification failure returned by `StoreKit`.
51        failure: VerificationFailure,
52    },
53}
54
55impl<T> VerificationResult<T> {
56    /// Returns `true` when `StoreKit` verified the payload.
57    pub const fn is_verified(&self) -> bool {
58        matches!(self, Self::Verified { .. })
59    }
60
61    /// Returns the decoded payload returned by `StoreKit`.
62    pub const fn payload(&self) -> &T {
63        match self {
64            Self::Verified { payload, .. } | Self::Unverified { payload, .. } => payload,
65        }
66    }
67
68    /// Consumes the wrapper and returns the decoded `StoreKit` payload.
69    pub fn into_payload(self) -> T {
70        match self {
71            Self::Verified { payload, .. } | Self::Unverified { payload, .. } => payload,
72        }
73    }
74
75    /// Returns the verification metadata returned by `StoreKit`.
76    pub const fn metadata(&self) -> &VerificationMetadata {
77        match self {
78            Self::Verified { metadata, .. } | Self::Unverified { metadata, .. } => metadata,
79        }
80    }
81
82    /// Returns the verification failure reported by `StoreKit`, if any.
83    pub const fn verification_failure(&self) -> Option<&VerificationFailure> {
84        match self {
85            Self::Verified { .. } => None,
86            Self::Unverified { failure, .. } => Some(failure),
87        }
88    }
89
90    /// Consumes the wrapper and returns the payload, metadata, and optional verification failure.
91    pub fn into_parts(self) -> (T, VerificationMetadata, Option<VerificationFailure>) {
92        match self {
93            Self::Verified { payload, metadata } => (payload, metadata, None),
94            Self::Unverified {
95                payload,
96                metadata,
97                failure,
98            } => (payload, metadata, Some(failure)),
99        }
100    }
101
102    /// Returns the compact JWS representation returned by `StoreKit`.
103    pub fn jws_representation(&self) -> &str {
104        self.metadata().jws_representation()
105    }
106}
107
108#[derive(Debug, Deserialize)]
109pub(crate) struct VerificationMetadataPayload {
110    #[serde(rename = "jwsRepresentation")]
111    jws_representation: String,
112    #[serde(rename = "headerDataBase64")]
113    header_data_base64: String,
114    #[serde(rename = "payloadDataBase64")]
115    payload_data_base64: String,
116    #[serde(rename = "signatureDataBase64")]
117    signature_data_base64: String,
118    #[serde(rename = "signedDataBase64")]
119    signed_data_base64: String,
120    #[serde(rename = "signedDate")]
121    signed_date: String,
122    #[serde(rename = "deviceVerificationBase64")]
123    device_verification_base64: String,
124    #[serde(rename = "deviceVerificationNonce")]
125    device_verification_nonce: String,
126}
127
128impl VerificationMetadataPayload {
129    fn into_metadata(self) -> Result<VerificationMetadata, StoreKitError> {
130        Ok(VerificationMetadata {
131            jws_representation: self.jws_representation,
132            header_data: decode_base64(&self.header_data_base64, "verification header data")?,
133            payload_data: decode_base64(&self.payload_data_base64, "verification payload data")?,
134            signature_data: decode_base64(
135                &self.signature_data_base64,
136                "verification signature data",
137            )?,
138            signed_data: decode_base64(&self.signed_data_base64, "verification signed data")?,
139            signed_date: self.signed_date,
140            device_verification: decode_base64(
141                &self.device_verification_base64,
142                "verification device data",
143            )?,
144            device_verification_nonce: self.device_verification_nonce,
145        })
146    }
147}
148
149#[derive(Debug, Deserialize)]
150pub(crate) struct VerificationResultPayload<T> {
151    kind: String,
152    payload: T,
153    metadata: VerificationMetadataPayload,
154    #[serde(rename = "verificationError")]
155    verification_error: Option<VerificationErrorPayload>,
156}
157
158impl<T> VerificationResultPayload<T> {
159    pub(crate) fn into_result<U, F>(
160        self,
161        map_payload: F,
162    ) -> Result<VerificationResult<U>, StoreKitError>
163    where
164        F: FnOnce(T) -> Result<U, StoreKitError>,
165    {
166        let payload = map_payload(self.payload)?;
167        let metadata = self.metadata.into_metadata()?;
168        match self.kind.as_str() {
169            "verified" => Ok(VerificationResult::Verified { payload, metadata }),
170            "unverified" => {
171                let failure = self
172                    .verification_error
173                    .map(VerificationFailure::from_payload)
174                    .ok_or_else(|| {
175                        StoreKitError::Unknown(
176                            "StoreKit returned an unverified result without a verification error"
177                                .to_owned(),
178                        )
179                    })?;
180                Ok(VerificationResult::Unverified {
181                    payload,
182                    metadata,
183                    failure,
184                })
185            }
186            other => Err(StoreKitError::Unknown(format!(
187                "StoreKit returned an unknown verification result kind '{other}'"
188            ))),
189        }
190    }
191}