Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

Commit

Permalink
working payment flow
Browse files Browse the repository at this point in the history
  • Loading branch information
Geometrically committed Aug 9, 2024
1 parent 5d731f9 commit 558e280
Show file tree
Hide file tree
Showing 7 changed files with 610 additions and 405 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,11 @@ jemallocator = {version = "0.5.4", optional = true}

async-stripe = { version = "0.37.3", features = ["runtime-tokio-hyper-rustls"] }
rusty-money = "0.4.1"
json-patch = "*"

[dev-dependencies]
actix-http = "3.4.0"
json-patch = "*"

[profile.dev]
opt-level = 0 # Minimal optimization, speeds up compilation
lto = false # Disables Link Time Optimization
Expand Down
7 changes: 3 additions & 4 deletions migrations/20240702213250_subscriptions.sql
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,15 @@ CREATE TABLE products (
CREATE TABLE products_prices (
id bigint PRIMARY KEY,
product_id bigint REFERENCES products NOT NULL,
interval jsonb NOT NULL,
-- price in smallest currency unit (cents for USD)
price int not null,
currency_code text not null
currency_code text not null,
prices jsonb NOT NULL
);

CREATE TABLE users_subscriptions (
id bigint PRIMARY KEY,
user_id bigint REFERENCES users NOT NULL,
price_id bigint REFERENCES products_prices NOT NULL,
interval text NOT NULL,
created timestamptz DEFAULT CURRENT_TIMESTAMP NOT NULL,
expires timestamptz DEFAULT CURRENT_TIMESTAMP NOT NULL,
last_charge timestamptz NULL,
Expand Down
16 changes: 6 additions & 10 deletions src/database/models/product_item.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::database::models::{product_item, DatabaseError, ProductId, ProductPriceId};
use crate::database::redis::RedisPool;
use crate::models::billing::{PriceInterval, ProductMetadata};
use crate::models::billing::{Price, ProductMetadata};
use dashmap::DashMap;
use itertools::Itertools;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -129,8 +129,7 @@ impl QueryProduct {
.map(|x| ProductPriceItem {
id: x.id,
product_id: x.product_id,
interval: x.interval,
price: x.price,
prices: x.prices,
currency_code: x.currency_code,
})
.collect(),
Expand All @@ -150,16 +149,14 @@ impl QueryProduct {
pub struct ProductPriceItem {
pub id: ProductPriceId,
pub product_id: ProductId,
pub interval: PriceInterval,
pub price: i32,
pub prices: Price,
pub currency_code: String,
}

struct ProductPriceResult {
id: i64,
product_id: i64,
interval: serde_json::Value,
price: i32,
prices: serde_json::Value,
currency_code: String,
}

Expand All @@ -168,7 +165,7 @@ macro_rules! select_prices_with_predicate {
sqlx::query_as!(
ProductPriceResult,
r#"
SELECT id, product_id, interval, price, currency_code
SELECT id, product_id, prices, currency_code
FROM products_prices
"#
+ $predicate,
Expand All @@ -184,8 +181,7 @@ impl TryFrom<ProductPriceResult> for ProductPriceItem {
Ok(ProductPriceItem {
id: ProductPriceId(r.id),
product_id: ProductId(r.product_id),
interval: serde_json::from_value(r.interval)?,
price: r.price,
prices: serde_json::from_value(r.prices)?,
currency_code: r.currency_code,
})
}
Expand Down
15 changes: 10 additions & 5 deletions src/database/models/user_subscription_item.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use crate::database::models::{DatabaseError, ProductPriceId, UserId, UserSubscriptionId};
use crate::models::billing::SubscriptionStatus;
use crate::models::billing::{PriceDuration, SubscriptionStatus};
use chrono::{DateTime, Utc};
use itertools::Itertools;

pub struct UserSubscriptionItem {
pub id: UserSubscriptionId,
pub user_id: UserId,
pub price_id: ProductPriceId,
pub interval: PriceDuration,
pub created: DateTime<Utc>,
pub expires: DateTime<Utc>,
pub last_charge: Option<DateTime<Utc>>,
Expand All @@ -17,6 +18,7 @@ struct UserSubscriptionResult {
id: i64,
user_id: i64,
price_id: i64,
interval: String,
pub created: DateTime<Utc>,
pub expires: DateTime<Utc>,
pub last_charge: Option<DateTime<Utc>>,
Expand All @@ -29,7 +31,7 @@ macro_rules! select_user_subscriptions_with_predicate {
UserSubscriptionResult,
r#"
SELECT
id, user_id, price_id, created, expires, last_charge, status
id, user_id, price_id, interval, created, expires, last_charge, status
FROM users_subscriptions
"#
+ $predicate,
Expand All @@ -44,6 +46,7 @@ impl From<UserSubscriptionResult> for UserSubscriptionItem {
id: UserSubscriptionId(r.id),
user_id: UserId(r.user_id),
price_id: ProductPriceId(r.price_id),
interval: PriceDuration::from_string(&r.interval),
created: r.created,
expires: r.expires,
last_charge: r.last_charge,
Expand Down Expand Up @@ -104,20 +107,22 @@ impl UserSubscriptionItem {
sqlx::query!(
"
INSERT INTO users_subscriptions (
id, user_id, price_id, created, expires, last_charge, status
id, user_id, price_id, interval, created, expires, last_charge, status
)
VALUES (
$1, $2, $3, $4, $5, $6, $7
$1, $2, $3, $4, $5, $6, $7, $8
)
ON CONFLICT (id)
DO UPDATE
SET expires = EXCLUDED.expires,
SET interval = EXCLUDED.interval,
expires = EXCLUDED.expires,
last_charge = EXCLUDED.last_charge,
status = EXCLUDED.status
",
self.id.0,
self.user_id.0,
self.price_id.0,
self.interval.as_str(),
self.created,
self.expires,
self.last_charge,
Expand Down
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,9 +266,9 @@ pub fn app_setup(
let redis_ref = redis_pool.clone();

Check warning on line 266 in src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

unused variable: `redis_ref`

warning: unused variable: `redis_ref` --> src/lib.rs:266:13 | 266 | let redis_ref = redis_pool.clone(); | ^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_redis_ref`
let stripe_client_ref = stripe_client.clone();

Check warning on line 267 in src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

unused variable: `stripe_client_ref`

warning: unused variable: `stripe_client_ref` --> src/lib.rs:267:13 | 267 | let stripe_client_ref = stripe_client.clone(); | ^^^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_stripe_client_ref`

actix_rt::spawn(async move {
routes::internal::billing::task(stripe_client_ref, pool_ref, redis_ref).await;
});
// actix_rt::spawn(async move {
// routes::internal::billing::task(stripe_client_ref, pool_ref, redis_ref).await;
// });
}

let ip_salt = Pepper {
Expand Down
41 changes: 28 additions & 13 deletions src/models/v3/billing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::models::ids::Base62Id;
use crate::models::ids::UserId;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
#[serde(from = "Base62Id")]
Expand Down Expand Up @@ -31,30 +32,43 @@ pub struct ProductPriceId(pub u64);
pub struct ProductPrice {
pub id: ProductPriceId,
pub product_id: ProductId,
pub interval: PriceInterval,
pub price: i32,
pub prices: Price,
pub currency_code: String,
}

#[derive(Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "kebab-case")]
pub enum PriceInterval {
OneTime,
/// For recurring payments. amount: 1 and duration: 'week' would result in a recurring payment
/// every week
pub enum Price {
OneTime {
price: i32,
},
Recurring {
amount: usize,
duration: PriceDuration,
intervals: HashMap<PriceDuration, i32>,
},
}

#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Hash, Eq, PartialEq, Debug, Copy, Clone)]
#[serde(rename_all = "kebab-case")]
pub enum PriceDuration {
Day,
Week,
Month,
Year,
Monthly,
Yearly,
}

impl PriceDuration {
pub fn from_string(string: &str) -> PriceDuration {
match string {
"monthly" => PriceDuration::Monthly,
"yearly" => PriceDuration::Yearly,
_ => PriceDuration::Monthly,
}
}

pub fn as_str(&self) -> &'static str {
match self {
PriceDuration::Monthly => "monthly",
PriceDuration::Yearly => "yearly",
}
}
}

#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
Expand All @@ -67,6 +81,7 @@ pub struct UserSubscription {
pub id: UserSubscriptionId,
pub user_id: UserId,
pub price_id: ProductPriceId,
pub interval: PriceDuration,
pub status: SubscriptionStatus,
pub created: DateTime<Utc>,
pub expires: DateTime<Utc>,
Expand Down
Loading

0 comments on commit 558e280

Please sign in to comment.