mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-14 05:42:32 +00:00
end and bill dates
This commit is contained in:
1 parent
0f5ec3c83b
commit
92a0674776
6 files changed
+79
-12
No files matched your search
+6
@@ -36,7 +36,12 @@ pub struct ActiveSubscriptionInfo {
|
||||
pub subscription_token: UserSubscriptionToken,
|
||||
pub product_slug: ArtcraftSubscriptionSlug,
|
||||
pub namespace: PaymentsNamespace,
|
||||
|
||||
/// If the subscription is active, this is the next bill date
|
||||
pub next_bill_at: Option<DateTime<Utc>>,
|
||||
|
||||
/// If the subscription is expired or is set to expire, this is the end date of the subscription.
|
||||
pub subscription_end_at: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
impl SerializeMarker for GetSubscriptionResponse {}
|
||||
@@ -89,6 +94,7 @@ async fn get(
|
||||
product_slug,
|
||||
namespace: sub.namespace,
|
||||
next_bill_at: sub.next_bill_at,
|
||||
subscription_end_at: sub.subscription_end_at,
|
||||
})
|
||||
})
|
||||
.flatten(),
|
||||
|
||||
+18
-1
@@ -34,6 +34,16 @@ pub struct UserSubscription {
|
||||
|
||||
/// When the current billing period ends (either auto-renew/rebill date or auto-cancel/cancellation date).
|
||||
pub current_billing_period_end_at: DateTime<Utc>,
|
||||
|
||||
/// If the subscription is set to expire (perhaps in the future), this is the date it will expire.
|
||||
/// TODO: Determine if this is set on expired, payment invalid, or canceled subscriptions where the date
|
||||
/// is backdated ahead of `maybe_canceled_at`.
|
||||
pub maybe_cancel_at: Option<DateTime<Utc>>,
|
||||
|
||||
/// This might be *before* the subscription is canceled, depending on stripe setup and
|
||||
/// the cancellation algorithm used. Eg. a year-long subscription that ends next year
|
||||
/// might be maybe_canceled_at a few days ago, but set to `maybe_cancel_at` next year.
|
||||
pub maybe_canceled_at: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
/// Technically, there may be more than one subscription record.
|
||||
@@ -89,6 +99,8 @@ fn map_result(result: Result<Option<RawUserSubscription>, sqlx::Error>) -> Resul
|
||||
subscription_start_at: record.subscription_start_at,
|
||||
subscription_expires_at: record.subscription_expires_at,
|
||||
current_billing_period_end_at: record.current_billing_period_end_at,
|
||||
maybe_cancel_at: record.maybe_cancel_at,
|
||||
maybe_canceled_at: record.maybe_canceled_at,
|
||||
})),
|
||||
Ok(None) => Ok(None),
|
||||
Err(e) => Err(e.into()),
|
||||
@@ -117,7 +129,9 @@ SELECT
|
||||
maybe_stripe_invoice_is_paid,
|
||||
subscription_start_at,
|
||||
subscription_expires_at,
|
||||
current_billing_period_end_at
|
||||
current_billing_period_end_at,
|
||||
maybe_cancel_at,
|
||||
maybe_canceled_at
|
||||
|
||||
FROM user_subscriptions
|
||||
|
||||
@@ -157,4 +171,7 @@ struct RawUserSubscription {
|
||||
subscription_expires_at: DateTime<Utc>,
|
||||
|
||||
current_billing_period_end_at: DateTime<Utc>,
|
||||
|
||||
maybe_cancel_at: Option<DateTime<Utc>>,
|
||||
maybe_canceled_at: Option<DateTime<Utc>>,
|
||||
}
|
||||
+30
-5
@@ -81,16 +81,41 @@ pub async fn get_session_subscription_handler(
|
||||
success: true,
|
||||
active_subscription: maybe_subscription.map(|sub| {
|
||||
let mut next_bill_date = None;
|
||||
|
||||
// TODO: Suppress if canceled.
|
||||
next_bill_date = Some(sub.current_billing_period_end_at);
|
||||
|
||||
let mut subscription_end_date = None;
|
||||
|
||||
// cancel_at > canceled_at - [normal] subscription was terminated and expires (after the canceled_at date, perhaps in the future)
|
||||
// cancel_at < canceled_at - [???] - ???
|
||||
|
||||
match (sub.maybe_cancel_at, sub.maybe_canceled_at) {
|
||||
(Some(cancel_at), Some(canceled_at)) => {
|
||||
// NB: `canceled_at` is when the user canceled the subscription, not necessarily when it expires.
|
||||
// TODO: Not sure this is the correct logic.
|
||||
subscription_end_date = Some(cancel_at);
|
||||
},
|
||||
(Some(cancel_at), None) => {
|
||||
// Canceling at end of period.
|
||||
subscription_end_date = Some(cancel_at);
|
||||
},
|
||||
(None, Some(canceled_at)) => {
|
||||
// Canceled already.
|
||||
subscription_end_date = Some(canceled_at);
|
||||
},
|
||||
(None, None) => {
|
||||
// Active subscription.
|
||||
next_bill_date = Some(sub.current_billing_period_end_at);
|
||||
},
|
||||
}
|
||||
|
||||
//if sub.maybe_cancel_at.is_none() && sub.maybe_canceled_at.is_none() {
|
||||
// next_bill_date = Some(sub.current_billing_period_end_at);
|
||||
//}
|
||||
|
||||
SubscriptionInfo {
|
||||
namespace: sub.subscription_namespace,
|
||||
product_slug: sub.subscription_product_slug,
|
||||
subscription_token: sub.token,
|
||||
next_bill_at: next_bill_date,
|
||||
subscription_end_at: None,
|
||||
subscription_end_at: subscription_end_date,
|
||||
}
|
||||
}),
|
||||
}))
|
||||
|
||||
@@ -31,6 +31,9 @@ export const BillingSettingsPane = (args: BillingSettingsPaneProps) => {
|
||||
|
||||
const hasPaidPlan = subscriptionStore.hasPaidPlan();
|
||||
|
||||
const nextBillAt = subscriptionStore.subscriptionInfo?.nextBillAt?.toLocaleDateString();
|
||||
const subscriptionEndAt = subscriptionStore.subscriptionInfo?.subscriptionEndAt?.toLocaleDateString();
|
||||
|
||||
useEffect(() => {
|
||||
creditsStore.fetchFromServer();
|
||||
subscriptionStore.fetchFromServer();
|
||||
@@ -70,11 +73,22 @@ export const BillingSettingsPane = (args: BillingSettingsPaneProps) => {
|
||||
{billingInfo.nextPayment.date}
|
||||
</div>
|
||||
*/}
|
||||
<div className="flex items-center gap-2 text-white/50">
|
||||
<FontAwesomeIcon icon={faInfoCircle} />
|
||||
Next payment on{" "}
|
||||
{subscriptionStore.subscriptionInfo?.nextBillAt?.toLocaleDateString()}
|
||||
</div>
|
||||
|
||||
{subscriptionEndAt && (
|
||||
<div className="flex items-center gap-2 text-white/50">
|
||||
<FontAwesomeIcon icon={faInfoCircle} />
|
||||
Subscription ends on{" "}
|
||||
{subscriptionEndAt}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{nextBillAt && (
|
||||
<div className="flex items-center gap-2 text-white/50">
|
||||
<FontAwesomeIcon icon={faInfoCircle} />
|
||||
Next payment on{" "}
|
||||
{nextBillAt}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<hr className="border-white/10" />
|
||||
|
||||
|
||||
@@ -24,8 +24,11 @@ interface SubscriptionInfo {
|
||||
/// The namespace for the subscription ("artcraft" or "fakeyou")
|
||||
namespace: string,
|
||||
|
||||
/// The next billing date for the subscription
|
||||
/// The next billing date for the subscription (if it's active and not set to expire/cancel)
|
||||
nextBillAt?: Date,
|
||||
|
||||
/// The end date for the subscription (if it's expired or set to expire/cancel)
|
||||
subscriptionEndAt?: Date,
|
||||
}
|
||||
|
||||
export const useSubscriptionState = create<SubscriptionState>()((set, get) => ({
|
||||
@@ -47,6 +50,7 @@ export const useSubscriptionState = create<SubscriptionState>()((set, get) => ({
|
||||
productSlug: data.payload.active_subscription.product_slug,
|
||||
namespace: data.payload.active_subscription.namespace,
|
||||
nextBillAt: data.payload.active_subscription.next_bill_at ? new Date(data.payload.active_subscription.next_bill_at) : undefined,
|
||||
subscriptionEndAt: data.payload.active_subscription.subscription_end_at ? new Date(data.payload.active_subscription.subscription_end_at) : undefined,
|
||||
};
|
||||
}
|
||||
console.log(">>> activeSubscription", activeSubscription);
|
||||
|
||||
@@ -14,6 +14,7 @@ export interface ActiveSubscriptionInfo {
|
||||
product_slug: string,
|
||||
namespace: string,
|
||||
next_bill_at?: string,
|
||||
subscription_end_at?: string,
|
||||
}
|
||||
|
||||
export const ArtcraftGetSubscription = async () : Promise<ArtcraftGetSubscriptionSuccess> => {
|
||||
|
||||
Reference in new issue
Block a user