1use rustauth_core::error::RustAuthError;
2use rustauth_core::error_codes::ErrorCode;
3use rustauth_core::plugin::PluginErrorCode;
4
5#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
7pub enum StripeConfigError {
8 #[error("stripe_webhook_secret must not be empty")]
9 EmptyWebhookSecret,
10 #[error(
11 "seat-based billing requires organization: {{ enabled: true }} in stripe plugin options"
12 )]
13 SeatPricingWithoutOrganization,
14}
15
16impl From<StripeConfigError> for RustAuthError {
17 fn from(error: StripeConfigError) -> Self {
18 Self::InvalidConfig(error.to_string())
19 }
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum StripeErrorCode {
24 Unauthorized,
25 InvalidRequestBody,
26 SubscriptionNotFound,
27 SubscriptionPlanNotFound,
28 AlreadySubscribedPlan,
29 ReferenceIdNotAllowed,
30 CustomerNotFound,
31 UnableToCreateCustomer,
32 UnableToCreateBillingPortal,
33 StripeSignatureNotFound,
34 StripeWebhookSecretNotFound,
35 StripeWebhookError,
36 FailedToConstructStripeEvent,
37 FailedToFetchPlans,
38 EmailVerificationRequired,
39 SubscriptionNotActive,
40 SubscriptionNotPendingChange,
41 OrganizationNotFound,
42 OrganizationSubscriptionNotEnabled,
43 AuthorizeReferenceRequired,
44 OrganizationHasActiveSubscription,
45 OrganizationReferenceIdRequired,
46}
47
48impl std::fmt::Display for StripeErrorCode {
49 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50 formatter.write_str(self.message())
51 }
52}
53
54impl StripeErrorCode {
55 pub fn code(self) -> &'static str {
56 match self {
57 Self::Unauthorized => "UNAUTHORIZED",
58 Self::InvalidRequestBody => "INVALID_REQUEST_BODY",
59 Self::SubscriptionNotFound => "SUBSCRIPTION_NOT_FOUND",
60 Self::SubscriptionPlanNotFound => "SUBSCRIPTION_PLAN_NOT_FOUND",
61 Self::AlreadySubscribedPlan => "ALREADY_SUBSCRIBED_PLAN",
62 Self::ReferenceIdNotAllowed => "REFERENCE_ID_NOT_ALLOWED",
63 Self::CustomerNotFound => "CUSTOMER_NOT_FOUND",
64 Self::UnableToCreateCustomer => "UNABLE_TO_CREATE_CUSTOMER",
65 Self::UnableToCreateBillingPortal => "UNABLE_TO_CREATE_BILLING_PORTAL",
66 Self::StripeSignatureNotFound => "STRIPE_SIGNATURE_NOT_FOUND",
67 Self::StripeWebhookSecretNotFound => "STRIPE_WEBHOOK_SECRET_NOT_FOUND",
68 Self::StripeWebhookError => "STRIPE_WEBHOOK_ERROR",
69 Self::FailedToConstructStripeEvent => "FAILED_TO_CONSTRUCT_STRIPE_EVENT",
70 Self::FailedToFetchPlans => "FAILED_TO_FETCH_PLANS",
71 Self::EmailVerificationRequired => "EMAIL_VERIFICATION_REQUIRED",
72 Self::SubscriptionNotActive => "SUBSCRIPTION_NOT_ACTIVE",
73 Self::SubscriptionNotPendingChange => "SUBSCRIPTION_NOT_PENDING_CHANGE",
74 Self::OrganizationNotFound => "ORGANIZATION_NOT_FOUND",
75 Self::OrganizationSubscriptionNotEnabled => "ORGANIZATION_SUBSCRIPTION_NOT_ENABLED",
76 Self::AuthorizeReferenceRequired => "AUTHORIZE_REFERENCE_REQUIRED",
77 Self::OrganizationHasActiveSubscription => "ORGANIZATION_HAS_ACTIVE_SUBSCRIPTION",
78 Self::OrganizationReferenceIdRequired => "ORGANIZATION_REFERENCE_ID_REQUIRED",
79 }
80 }
81
82 pub fn message(self) -> &'static str {
83 match self {
84 Self::Unauthorized => "Unauthorized access",
85 Self::InvalidRequestBody => "Invalid request body",
86 Self::SubscriptionNotFound => "Subscription not found",
87 Self::SubscriptionPlanNotFound => "Subscription plan not found",
88 Self::AlreadySubscribedPlan => "You're already subscribed to this plan",
89 Self::ReferenceIdNotAllowed => "Reference id is not allowed",
90 Self::CustomerNotFound => "Stripe customer not found for this user",
91 Self::UnableToCreateCustomer => "Unable to create customer",
92 Self::UnableToCreateBillingPortal => "Unable to create billing portal session",
93 Self::StripeSignatureNotFound => "Stripe signature not found",
94 Self::StripeWebhookSecretNotFound => "Stripe webhook secret not found",
95 Self::StripeWebhookError => "Stripe webhook error",
96 Self::FailedToConstructStripeEvent => "Failed to construct Stripe event",
97 Self::FailedToFetchPlans => "Failed to fetch plans",
98 Self::EmailVerificationRequired => {
99 "Email verification is required before you can subscribe to a plan"
100 }
101 Self::SubscriptionNotActive => "Subscription is not active",
102 Self::SubscriptionNotPendingChange => {
103 "Subscription has no pending cancellation or scheduled plan change"
104 }
105 Self::OrganizationNotFound => "Organization not found",
106 Self::OrganizationSubscriptionNotEnabled => "Organization subscription is not enabled",
107 Self::AuthorizeReferenceRequired => {
108 "Organization subscriptions require authorizeReference callback to be configured"
109 }
110 Self::OrganizationHasActiveSubscription => {
111 "Cannot delete organization with active subscription"
112 }
113 Self::OrganizationReferenceIdRequired => {
114 "Reference ID is required. Provide referenceId or set activeOrganizationId in session"
115 }
116 }
117 }
118}
119
120impl ErrorCode for StripeErrorCode {
121 fn as_str(&self) -> &str {
122 (*self).code()
123 }
124
125 fn message(&self) -> &str {
126 (*self).message()
127 }
128}
129
130pub fn error_codes() -> Vec<PluginErrorCode> {
131 [
132 StripeErrorCode::Unauthorized,
133 StripeErrorCode::InvalidRequestBody,
134 StripeErrorCode::SubscriptionNotFound,
135 StripeErrorCode::SubscriptionPlanNotFound,
136 StripeErrorCode::AlreadySubscribedPlan,
137 StripeErrorCode::ReferenceIdNotAllowed,
138 StripeErrorCode::CustomerNotFound,
139 StripeErrorCode::UnableToCreateCustomer,
140 StripeErrorCode::UnableToCreateBillingPortal,
141 StripeErrorCode::StripeSignatureNotFound,
142 StripeErrorCode::StripeWebhookSecretNotFound,
143 StripeErrorCode::StripeWebhookError,
144 StripeErrorCode::FailedToConstructStripeEvent,
145 StripeErrorCode::FailedToFetchPlans,
146 StripeErrorCode::EmailVerificationRequired,
147 StripeErrorCode::SubscriptionNotActive,
148 StripeErrorCode::SubscriptionNotPendingChange,
149 StripeErrorCode::OrganizationNotFound,
150 StripeErrorCode::OrganizationSubscriptionNotEnabled,
151 StripeErrorCode::AuthorizeReferenceRequired,
152 StripeErrorCode::OrganizationHasActiveSubscription,
153 StripeErrorCode::OrganizationReferenceIdRequired,
154 ]
155 .into_iter()
156 .map(|code| PluginErrorCode::new(code.code(), code.message()))
157 .collect()
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163 use rustauth_core::error_codes::ErrorCode;
164
165 fn assert_error_code(code: impl ErrorCode, expected_code: &str, expected_message: &str) {
166 assert_eq!(code.as_str(), expected_code);
167 assert_eq!(code.message(), expected_message);
168 }
169
170 #[test]
171 fn stripe_error_code_implements_error_code_trait() {
172 assert_error_code(
173 StripeErrorCode::SubscriptionNotFound,
174 "SUBSCRIPTION_NOT_FOUND",
175 "Subscription not found",
176 );
177 }
178}