Add receipt to DB to prevent double-use.
This commit is contained in:
parent
7d78302427
commit
861c6731c7
4 changed files with 58 additions and 23 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -2018,9 +2018,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "whoami"
|
||||
version = "1.5.0"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fec781d48b41f8163426ed18e8fc2864c12937df9ce54c88ede7bd47270893e"
|
||||
checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9"
|
||||
dependencies = [
|
||||
"redox_syscall",
|
||||
"wasite",
|
||||
|
|
|
@ -2,9 +2,9 @@ drop table if exists customers;
|
|||
drop index if exists customers_username_dex;
|
||||
drop index if exists customers_email_dex;
|
||||
drop index if exists customers_invitation_dex;
|
||||
drop trigger if exists update_last_updated_customers;
|
||||
drop trigger if exists update_customers_updated_at;
|
||||
|
||||
drop table if exists invitations;
|
||||
drop index if exists invitations_owner_dex;
|
||||
drop trigger if exists update_updated_at_invitations;
|
||||
drop trigger if exists update_invitations_updated_at;
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ create index if not exists customers_email_dex on customers (lower(billing_email
|
|||
create index if not exists customers_receipt_dex on customers (receipt);
|
||||
create index if not exists customers_invitation_dex on customers (invitation); -- does this need to be created? it's already a foreign key
|
||||
|
||||
create trigger if not exists update_last_updated_customers
|
||||
create trigger if not exists update_customers_updated_at
|
||||
after update on customers
|
||||
when OLD.updated_at = NEW.updated_at or OLD.updated_at is null
|
||||
BEGIN
|
||||
|
@ -32,7 +32,7 @@ create table if not exists invitations (
|
|||
);
|
||||
create index if not exists invitations_owner_dex on invitations (owner);
|
||||
|
||||
create trigger if not exists update_updated_at_invitations
|
||||
create trigger if not exists update_invitations_updated_at
|
||||
after update on invitations
|
||||
when OLD.updated_at = NEW.updated_at or OLD.updated_at is null
|
||||
BEGIN
|
||||
|
|
|
@ -27,7 +27,11 @@ lazy_static! {
|
|||
static ref MONTHLY_LINK: String = std::env::var("MONTHLY_LINK").unwrap();
|
||||
}
|
||||
|
||||
/// Displays the signup form.
|
||||
//-************************************************************************
|
||||
// handlers: get_signup, post_signup, and payment_success
|
||||
//-************************************************************************
|
||||
|
||||
/// Displays the signup page with links to Stripe
|
||||
pub async fn get_signup() -> impl IntoResponse {
|
||||
SignupPage {
|
||||
monthly_link: Some((*MONTHLY_LINK).to_string()),
|
||||
|
@ -40,36 +44,55 @@ pub async fn post_signup(
|
|||
State(db): State<SqlitePool>,
|
||||
Form(form): Form<SignupForm>,
|
||||
) -> Result<impl IntoResponse, CreateUserError> {
|
||||
let receipt = form.receipt.trim();
|
||||
if confirm_payment(&db, receipt).await {
|
||||
log::info!("Confirmed payment again from {receipt}");
|
||||
} else {
|
||||
log::warn!("Attempt to use duplicate receipt {receipt}");
|
||||
return Err(CreateUserError(CreateUserErrorKind::BadPayment));
|
||||
}
|
||||
|
||||
let user = validate_signup(&form).await?;
|
||||
if create_user(&user) {
|
||||
log::info!("Created user {user:?}");
|
||||
insert_user(&db, &form.username, form.receipt.trim(), None).await;
|
||||
insert_customer(&db, &form.username, form.receipt.trim(), None).await;
|
||||
Ok(SignupSuccessPage(user))
|
||||
} else {
|
||||
Err(CreateUserError(CreateUserErrorKind::UnknownEorr))
|
||||
}
|
||||
}
|
||||
|
||||
/// Redirected from Stripe with the receipt of payment.
|
||||
pub async fn payment_success(receipt: Option<Path<String>>) -> impl IntoResponse {
|
||||
/// Redirected from Stripe with the receipt of payment, and shows the signup
|
||||
/// form for creating your account.
|
||||
pub async fn payment_success(
|
||||
State(db): State<SqlitePool>,
|
||||
receipt: Option<Path<String>>,
|
||||
) -> impl IntoResponse {
|
||||
let receipt = if let Some(Path(receipt)) = receipt {
|
||||
receipt
|
||||
} else {
|
||||
return CreateUserError(CreateUserErrorKind::BadPayment).into_response();
|
||||
};
|
||||
let receipt = receipt.trim();
|
||||
if confirm_payment(&db, receipt).await {
|
||||
log::info!("Confirmed payment from {receipt}");
|
||||
} else {
|
||||
log::warn!("Attempt to use duplicate receipt {receipt}");
|
||||
return CreateUserError(CreateUserErrorKind::BadPayment).into_response();
|
||||
}
|
||||
|
||||
UserFormPage {
|
||||
receipt,
|
||||
receipt: receipt.to_string(),
|
||||
..Default::default()
|
||||
}
|
||||
.into_response()
|
||||
}
|
||||
|
||||
//-************************************************************************
|
||||
// helpers
|
||||
// private helpers for the handlers
|
||||
//-************************************************************************
|
||||
async fn insert_user(db: &SqlitePool, username: &str, receipt: &str, invitation: Option<&str>) {
|
||||
sqlx::query!(
|
||||
async fn insert_customer(db: &SqlitePool, username: &str, receipt: &str, invitation: Option<&str>) {
|
||||
match sqlx::query!(
|
||||
"insert into customers (username, receipt, invitation) values (?, ?, ?)",
|
||||
username,
|
||||
receipt,
|
||||
|
@ -77,7 +100,12 @@ async fn insert_user(db: &SqlitePool, username: &str, receipt: &str, invitation:
|
|||
)
|
||||
.execute(db)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
{
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
log::error!("Could not insert {receipt} for {username} into DB, got {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_user(user: &User) -> bool {
|
||||
|
@ -100,7 +128,21 @@ fn create_user(user: &User) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn confirm_payment(stripe_checkout_session_id: &str) -> bool {
|
||||
async fn confirm_payment(db: &SqlitePool, stripe_checkout_session_id: &str) -> bool {
|
||||
// first check the receipt to see that it hasn't been used already
|
||||
match sqlx::query_scalar!(
|
||||
"select count(*) from customers where receipt = ?",
|
||||
stripe_checkout_session_id
|
||||
)
|
||||
.fetch_one(db)
|
||||
.await
|
||||
.unwrap_or(0)
|
||||
{
|
||||
0 => {}
|
||||
_ => return false,
|
||||
}
|
||||
|
||||
// ok see if Stripe knows about it
|
||||
let token = &*STRIPE_TOKEN;
|
||||
let url = format!("https://api.stripe.com/v1/checkout/sessions/{stripe_checkout_session_id}");
|
||||
let json: serde_json::Value = ureq::get(&url)
|
||||
|
@ -130,13 +172,6 @@ async fn validate_signup(form: &SignupForm) -> Result<User, CreateUserError> {
|
|||
let username = form.username.trim();
|
||||
let password = form.password.trim();
|
||||
let verify = form.pw_verify.trim();
|
||||
let receipt = form.receipt.trim();
|
||||
|
||||
if confirm_payment(receipt) {
|
||||
log::info!("Confirmed payment from {receipt}");
|
||||
} else {
|
||||
return Err(CreateUserError(CreateUserErrorKind::BadPayment));
|
||||
}
|
||||
|
||||
let name_len = username.graphemes(true).size_hint().1.unwrap_or(0);
|
||||
// we are not ascii exclusivists around here
|
||||
|
|
Loading…
Reference in a new issue