mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 09:28:11 +00:00
next payment
This commit is contained in:
1 parent
9835a238ce
commit
0f5ec3c83b
9 files changed
+56
-9
No files matched your search
@@ -84,7 +84,10 @@ CREATE TABLE user_subscriptions (
|
||||
-- This may predate the Stripe object `created` timestamp due to backdating.
|
||||
subscription_start_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- We'll use this to determine if the subscription is active.
|
||||
-- Our synthetic "expire" date. Moved forward for active subscriptions,
|
||||
-- backdated for canceled subscriptions.
|
||||
--
|
||||
-- TODO: We'll (maybe?) use this to determine if the subscription is active.
|
||||
--
|
||||
-- Always compare against MySQL's clock rather than the app's clock so that we
|
||||
-- don't get weird clock skew behaviors across multiple requests.
|
||||
@@ -94,8 +97,12 @@ CREATE TABLE user_subscriptions (
|
||||
subscription_expires_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- Billing periods.
|
||||
-- Maybe useful for debugging.
|
||||
-- Stripe API: subscription.items[0].data.current_period_start
|
||||
current_billing_period_start_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- Stripe API: subscription.items[0].data.current_period_end
|
||||
-- current_period_end is when the subscription renews (or cancels, if the
|
||||
-- subscription is set to cancel).
|
||||
current_billing_period_end_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- Subscription cancellation (future and past)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use enums::common::payments_namespace::PaymentsNamespace;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use tokens::tokens::user_subscriptions::UserSubscriptionToken;
|
||||
@@ -23,4 +24,14 @@ pub struct SubscriptionInfo {
|
||||
|
||||
/// Local key for the subscription.
|
||||
pub product_slug: String,
|
||||
|
||||
/// If the subscription is currently active, this is the next billing date.
|
||||
pub next_bill_at: Option<DateTime<Utc>>,
|
||||
|
||||
/// If the subscription is set to cancel, this is the end date of the subscription.
|
||||
pub subscription_end_at: Option<DateTime<Utc>>,
|
||||
|
||||
///// When the current billing period ends (either
|
||||
///// auto-renew/rebill date or auto-cancel/cancellation date).
|
||||
//pub current_billing_period_end_at: DateTime<Utc>,
|
||||
}
|
||||
@@ -22,9 +22,7 @@ pub async fn get_session_subscription(
|
||||
).await?)
|
||||
}
|
||||
|
||||
|
||||
fn get_url_path(api_host: &ApiHost, payments_namespace: PaymentsNamespace) -> String {
|
||||
let payments_namespace = payments_namespace.to_str();
|
||||
format!("/v1/subscriptions/namespace/{}", payments_namespace)
|
||||
}
|
||||
|
||||
+3
@@ -1,3 +1,4 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::commands::providers::get_provider_order_command::GetProviderOrderResponse;
|
||||
use crate::core::commands::response::shorthand::ResponseOrErrorMessage;
|
||||
@@ -35,6 +36,7 @@ pub struct ActiveSubscriptionInfo {
|
||||
pub subscription_token: UserSubscriptionToken,
|
||||
pub product_slug: ArtcraftSubscriptionSlug,
|
||||
pub namespace: PaymentsNamespace,
|
||||
pub next_bill_at: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
impl SerializeMarker for GetSubscriptionResponse {}
|
||||
@@ -86,6 +88,7 @@ async fn get(
|
||||
subscription_token: sub.subscription_token,
|
||||
product_slug,
|
||||
namespace: sub.namespace,
|
||||
next_bill_at: sub.next_bill_at,
|
||||
})
|
||||
})
|
||||
.flatten(),
|
||||
|
||||
+8
-1
@@ -31,6 +31,9 @@ pub struct UserSubscription {
|
||||
|
||||
/// This is the authoritative timestamp for when the subscription expires.
|
||||
pub subscription_expires_at: DateTime<Utc>,
|
||||
|
||||
/// When the current billing period ends (either auto-renew/rebill date or auto-cancel/cancellation date).
|
||||
pub current_billing_period_end_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
/// Technically, there may be more than one subscription record.
|
||||
@@ -85,6 +88,7 @@ fn map_result(result: Result<Option<RawUserSubscription>, sqlx::Error>) -> Resul
|
||||
stripe_invoice_is_paid: nullable_i8_to_bool_default_false(record.maybe_stripe_invoice_is_paid),
|
||||
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,
|
||||
})),
|
||||
Ok(None) => Ok(None),
|
||||
Err(e) => Err(e.into()),
|
||||
@@ -112,7 +116,8 @@ SELECT
|
||||
maybe_stripe_subscription_status,
|
||||
maybe_stripe_invoice_is_paid,
|
||||
subscription_start_at,
|
||||
subscription_expires_at
|
||||
subscription_expires_at,
|
||||
current_billing_period_end_at
|
||||
|
||||
FROM user_subscriptions
|
||||
|
||||
@@ -150,4 +155,6 @@ struct RawUserSubscription {
|
||||
|
||||
subscription_start_at: DateTime<Utc>,
|
||||
subscription_expires_at: DateTime<Utc>,
|
||||
|
||||
current_billing_period_end_at: DateTime<Utc>,
|
||||
}
|
||||
+13
-4
@@ -79,10 +79,19 @@ pub async fn get_session_subscription_handler(
|
||||
|
||||
Ok(Json(GetSessionSubscriptionResponse {
|
||||
success: true,
|
||||
active_subscription: maybe_subscription.map(|sub| SubscriptionInfo {
|
||||
namespace: sub.subscription_namespace,
|
||||
product_slug: sub.subscription_product_slug,
|
||||
subscription_token: sub.token,
|
||||
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);
|
||||
|
||||
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,
|
||||
}
|
||||
}),
|
||||
}))
|
||||
}
|
||||
@@ -70,6 +70,11 @@ 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>
|
||||
|
||||
<hr className="border-white/10" />
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ interface SubscriptionInfo {
|
||||
|
||||
/// The namespace for the subscription ("artcraft" or "fakeyou")
|
||||
namespace: string,
|
||||
|
||||
/// The next billing date for the subscription
|
||||
nextBillAt?: Date,
|
||||
}
|
||||
|
||||
export const useSubscriptionState = create<SubscriptionState>()((set, get) => ({
|
||||
@@ -37,13 +40,16 @@ export const useSubscriptionState = create<SubscriptionState>()((set, get) => ({
|
||||
const data = await ArtcraftGetSubscription();
|
||||
if (!!data.payload) {
|
||||
let activeSubscription = undefined;
|
||||
console.log(">>> subscription data.payload", data.payload);
|
||||
if (!!data.payload.active_subscription) {
|
||||
activeSubscription = {
|
||||
subscriptionToken: data.payload.active_subscription.subscription_token,
|
||||
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,
|
||||
};
|
||||
}
|
||||
console.log(">>> activeSubscription", activeSubscription);
|
||||
set((state) => ({
|
||||
subscriptionInfo: activeSubscription,
|
||||
}));
|
||||
|
||||
@@ -13,6 +13,7 @@ export interface ActiveSubscriptionInfo {
|
||||
subscription_token: string,
|
||||
product_slug: string,
|
||||
namespace: string,
|
||||
next_bill_at?: string,
|
||||
}
|
||||
|
||||
export const ArtcraftGetSubscription = async () : Promise<ArtcraftGetSubscriptionSuccess> => {
|
||||
|
||||
Reference in new issue
Block a user