mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 17:38:10 +00:00
Stripe WIP
This commit is contained in:
1 parent
360a389e3d
commit
02e7d1f4fa
7 files changed
+181
-71
No files matched your search
@@ -104,7 +104,7 @@ rust-s3 = { version = "0.32.3" }
|
||||
subprocess = "0.2.6"
|
||||
tempdir = "0.3.7"
|
||||
|
||||
actix = "0.12.0"
|
||||
#actix = "0.12.0"
|
||||
actix-cors = "0.6.0-beta.3"
|
||||
actix-files= "0.6.0-beta.8"
|
||||
actix-http = "3.0.0-beta.11"
|
||||
@@ -120,6 +120,11 @@ jwt = "0.13.0"
|
||||
# NB: Furthermore, the Tokio runtime is old, so I have to use an older Hyper.
|
||||
hyper = { version = "0.14.14" }
|
||||
|
||||
# Money
|
||||
async-stripe = { version = "0.15.0", features = ["runtime-tokio-hyper"] }
|
||||
#async-stripe = { version = "0.15.0", features = ["runtime-blocking"] } # TODO: Determine proper runtime to use
|
||||
#dolladollabills = "0.2.0" # money from stripe
|
||||
|
||||
# Crypto
|
||||
bcrypt = "0.8" # password hashes
|
||||
hmac = "0.10.0" # cookies
|
||||
|
||||
@@ -8,6 +8,7 @@ pub mod leaderboard;
|
||||
pub mod misc;
|
||||
pub mod moderation;
|
||||
pub mod service;
|
||||
pub mod stripe;
|
||||
pub mod tts;
|
||||
pub mod twitch;
|
||||
pub mod vocoders;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod stripe_checkout_webhook;
|
||||
pub mod stripe_create_checkout_session_handler;
|
||||
pub mod stripe_common;
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::Path;
|
||||
use actix_web::{web, HttpResponse, HttpRequest};
|
||||
use chrono::{DateTime, Utc};
|
||||
use crate::utils::session_checker::SessionChecker;
|
||||
use http_server_common::request::get_request_header_optional::get_request_header_optional;
|
||||
use http_server_common::response::serialize_as_json_error::serialize_as_json_error;
|
||||
use http_server_common::util::timer::MultiBenchmarkingTimer;
|
||||
use log::warn;
|
||||
use reusable_types::entity_visibility::EntityVisibility;
|
||||
use sqlx::MySqlPool;
|
||||
use std::fmt;
|
||||
use actix_web::error::UrlencodedError::ContentType;
|
||||
|
||||
// TODO: This is duplicated in query_user_CreateCheckoutSession
|
||||
// TODO: This handler has embedded queries.
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct CreateCheckoutSessionSuccessResponse {
|
||||
pub success: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum CreateCheckoutSessionError {
|
||||
ServerError,
|
||||
}
|
||||
|
||||
impl ResponseError for CreateCheckoutSessionError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
CreateCheckoutSessionError::ServerError=> StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
serialize_as_json_error(self)
|
||||
}
|
||||
}
|
||||
|
||||
// NB: Not using derive_more::Display since Clion doesn't understand it.
|
||||
impl fmt::Display for CreateCheckoutSessionError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_checkout_session_handler(
|
||||
http_request: HttpRequest,
|
||||
mysql_pool: web::Data<MySqlPool>,
|
||||
) -> Result<HttpResponse, CreateCheckoutSessionError>
|
||||
{
|
||||
|
||||
let stripe_publishable_key = "pk_test_51KZvWXEU5se17Mekx6e0gw32ovDF78k2eeaGctLIs8oQVxN6DerEp1BDqh6gLMYKli4VsTZoVGHoxq5RmwKMlxtP00r59UEEad";
|
||||
let stripe_secret_key = "sk_test_51KZvWXEU5se17Mek11ZdnWr4frnyw7tWOPuvl3pRzL290zvkT6KqcbGUl3fcvc3hA8oH6lWXz2kGWXXUlNcCuDxB003uVT9JX0";
|
||||
|
||||
let product_fakeyou_basic_id = "prod_MMxi2J5y69VPbO";
|
||||
|
||||
|
||||
let response = CreateCheckoutSessionSuccessResponse {
|
||||
success: true,
|
||||
};
|
||||
|
||||
let body = serde_json::to_string(&response)
|
||||
.map_err(|_e| CreateCheckoutSessionError::ServerError)?;
|
||||
|
||||
Ok(HttpResponse::Ok()
|
||||
.content_type("application/json")
|
||||
.body(body))
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
// NB: These are "test" keys and not secret.
|
||||
pub const STRIPE_PUBLISHABLE_KEY : &'static str = "pk_test_51KZvWXEU5se17Mekx6e0gw32ovDF78k2eeaGctLIs8oQVxN6DerEp1BDqh6gLMYKli4VsTZoVGHoxq5RmwKMlxtP00r59UEEad";
|
||||
pub const STRIPE_SECRET_KEY : &'static str = "sk_test_51KZvWXEU5se17Mek11ZdnWr4frnyw7tWOPuvl3pRzL290zvkT6KqcbGUl3fcvc3hA8oH6lWXz2kGWXXUlNcCuDxB003uVT9JX0";
|
||||
|
||||
// NB: These are "test" product IDs.
|
||||
pub const PRODUCT_FAKEYOU_BASIC_ID : &'static str = "prod_MMxi2J5y69VPbO";
|
||||
pub const PRODUCT_FAKEYOU_BASIC_PRICE_ID : &'static str = "price_1LeDnKEU5se17MekVr1iYYNf";
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
use std::collections::HashMap;
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::error::UrlencodedError::ContentType;
|
||||
use actix_web::http::{header, StatusCode};
|
||||
use actix_web::web::Path;
|
||||
use actix_web::{web, HttpResponse, HttpRequest};
|
||||
use chrono::{DateTime, Utc};
|
||||
use crate::http_server::endpoints::stripe::stripe_common::{PRODUCT_FAKEYOU_BASIC_PRICE_ID, STRIPE_SECRET_KEY};
|
||||
use http_server_common::request::get_request_header_optional::get_request_header_optional;
|
||||
use http_server_common::response::serialize_as_json_error::serialize_as_json_error;
|
||||
use http_server_common::util::timer::MultiBenchmarkingTimer;
|
||||
use log::{error, warn};
|
||||
use reusable_types::entity_visibility::EntityVisibility;
|
||||
use sqlx::MySqlPool;
|
||||
use std::fmt;
|
||||
use stripe::{CheckoutSession, CheckoutSessionMode, CreateCheckoutSession, CreateCheckoutSessionLineItems};
|
||||
|
||||
// TODO: This is duplicated in query_user_CreateCheckoutSession
|
||||
// TODO: This handler has embedded queries.
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct CreateCheckoutSessionSuccessResponse {
|
||||
pub success: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum CreateCheckoutSessionError {
|
||||
ServerError,
|
||||
}
|
||||
|
||||
impl ResponseError for CreateCheckoutSessionError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
CreateCheckoutSessionError::ServerError=> StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
serialize_as_json_error(self)
|
||||
}
|
||||
}
|
||||
|
||||
// NB: Not using derive_more::Display since Clion doesn't understand it.
|
||||
impl fmt::Display for CreateCheckoutSessionError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn stripe_create_checkout_session_handler(
|
||||
http_request: HttpRequest,
|
||||
mysql_pool: web::Data<MySqlPool>,
|
||||
) -> Result<HttpResponse, CreateCheckoutSessionError>
|
||||
{
|
||||
//let stripe = Client::new(STRIPE_SECRET_KEY);
|
||||
//let checkout = stripe.checkout();
|
||||
|
||||
let stripe_client = stripe::Client::new(STRIPE_SECRET_KEY);
|
||||
|
||||
let mut metadata = HashMap::new();
|
||||
metadata.insert("user_token".to_string(), "U:TEST".to_string());
|
||||
|
||||
/*let params = CreateCheckoutSession {
|
||||
after_expiration: None,
|
||||
allow_promotion_codes: None,
|
||||
automatic_tax: None,
|
||||
billing_address_collection: None,
|
||||
cancel_url: "http://localhost:12345/stripe/checkout/cancel",
|
||||
client_reference_id: None,
|
||||
consent_collection: None,
|
||||
customer: None,
|
||||
customer_creation: None,
|
||||
customer_email: None,
|
||||
customer_update: None,
|
||||
discounts: None,
|
||||
expand: &[],
|
||||
expires_at: None,
|
||||
line_items: None,
|
||||
locale: None,
|
||||
metadata: Some(metadata),
|
||||
mode: Some(CheckoutSessionMode::Subscription),
|
||||
payment_intent_data: None,
|
||||
payment_method_options: None,
|
||||
payment_method_types: None,
|
||||
phone_number_collection: None,
|
||||
setup_intent_data: None,
|
||||
shipping_address_collection: None,
|
||||
shipping_options: None,
|
||||
submit_type: None,
|
||||
subscription_data: None,
|
||||
success_url: "http://localhost:12345/stripe/checkout/success",
|
||||
tax_id_collection: None
|
||||
};*/
|
||||
|
||||
|
||||
let checkout_session = {
|
||||
let mut params = CreateCheckoutSession::new("http://localhost:12345/stripe/checkout/cancel", "http://localhost:12345/stripe/checkout/success");
|
||||
|
||||
params.mode = Some(CheckoutSessionMode::Subscription);
|
||||
params.line_items = Some(vec![
|
||||
CreateCheckoutSessionLineItems {
|
||||
price: Some(PRODUCT_FAKEYOU_BASIC_PRICE_ID.to_string()),
|
||||
quantity: Some(1),
|
||||
..Default::default()
|
||||
}
|
||||
]);
|
||||
params.expand = &["line_items", "line_items.data.price.product"];
|
||||
|
||||
CheckoutSession::create(&stripe_client, params)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("Error: {:?}", e);
|
||||
CreateCheckoutSessionError::ServerError
|
||||
})?
|
||||
};
|
||||
|
||||
let url = checkout_session.url.expect("must have url");
|
||||
|
||||
//let response = CreateCheckoutSessionSuccessResponse {
|
||||
// success: true,
|
||||
//};
|
||||
|
||||
//let body = serde_json::to_string(&response)
|
||||
// .map_err(|_e| CreateCheckoutSessionError::ServerError)?;
|
||||
|
||||
//Ok(HttpResponse::Ok()
|
||||
// .content_type("application/json")
|
||||
// .body(body))
|
||||
|
||||
Ok(HttpResponse::Found()
|
||||
.append_header((header::LOCATION, url.to_string()))
|
||||
.finish())
|
||||
}
|
||||
@@ -92,6 +92,7 @@ use crate::http_server::endpoints::w2l::set_w2l_template_mod_approval::set_w2l_t
|
||||
use users_component::default_routes::add_suggested_api_v1_account_creation_and_session_routes;
|
||||
use users_component::endpoints::edit_profile_handler::edit_profile_handler;
|
||||
use users_component::endpoints::get_profile_handler::get_profile_handler;
|
||||
use crate::http_server::endpoints::stripe::stripe_create_checkout_session_handler::stripe_create_checkout_session_handler;
|
||||
|
||||
pub fn add_routes<T, B> (app: App<T, B>) -> App<T, B>
|
||||
where
|
||||
@@ -116,6 +117,7 @@ pub fn add_routes<T, B> (app: App<T, B>) -> App<T, B>
|
||||
app = add_twitch_routes(app); /* /twitch */ // TODO: MAYBE TEMPORARY
|
||||
app = add_investor_demo_routes(app); /* /demo_mode */ // TODO: DEFINITELY TEMPORARY
|
||||
app = add_flag_routes(app); /* /flag */
|
||||
app = add_stripe_routes(app); /* /flag */
|
||||
|
||||
// From components
|
||||
app = add_suggested_api_v1_account_creation_and_session_routes(app); // /create_account, /session, /login, /logout
|
||||
@@ -730,6 +732,34 @@ fn add_flag_routes<T, B> (app: App<T, B>) -> App<T, B>
|
||||
)
|
||||
}
|
||||
|
||||
// ==================== STRIPE ROUTES ====================
|
||||
|
||||
fn add_stripe_routes<T, B> (app: App<T, B>) -> App<T, B>
|
||||
where
|
||||
B: MessageBody,
|
||||
T: ServiceFactory<
|
||||
ServiceRequest,
|
||||
Config = (),
|
||||
Response = ServiceResponse<B>,
|
||||
Error = Error,
|
||||
InitError = (),
|
||||
>,
|
||||
{
|
||||
app.service(web::scope("/stripe")
|
||||
.service(web::scope("/checkout")
|
||||
.service(web::resource("/begin")
|
||||
.route(web::get().to(stripe_create_checkout_session_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
//.service(web::resource("/webhook")
|
||||
// .route(web::get().to())
|
||||
// .route(web::head().to(|| HttpResponse::Ok()))
|
||||
//)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// ==================== TWITCH ROUTES ====================
|
||||
|
||||
// TODO: Maybe move these into an "oauth-gateway" type http service.
|
||||
|
||||
Reference in new issue
Block a user