From ffc070ed519b0afda97c92cff8f0bc3a6f69f117 Mon Sep 17 00:00:00 2001 From: ntr Date: Thu, 29 Aug 2019 12:43:06 +1000 Subject: [PATCH] email and sub status --- WORKLOG.md | 8 ++- client/assets/styles/account.less | 3 +- client/src/actions.jsx | 1 + client/src/components/account.management.jsx | 39 ++++++++++----- client/src/components/account.status.jsx | 15 +++++- client/src/events.jsx | 5 ++ client/src/reducers.jsx | 2 + client/src/socket.jsx | 19 +++++-- server/src/payments.rs | 52 +++++++++++++++----- server/src/rpc.rs | 28 +++++------ 10 files changed, 124 insertions(+), 48 deletions(-) diff --git a/WORKLOG.md b/WORKLOG.md index 7860dae3..bc56173d 100644 --- a/WORKLOG.md +++ b/WORKLOG.md @@ -3,7 +3,9 @@ *PRODUCTION* * ACP * essential - * stripe verify + * serde serialize privatise + * DO postgres + * mobile styles * treats * constructs jiggle when clicked @@ -11,12 +13,8 @@ * bot game grind -* serde serialize privatise -* mobile styles * change score to enum * pct based translates for combat animation -* acp init -* DO postgres * make our own toasts / msg pane * send account_instances on players update diff --git a/client/assets/styles/account.less b/client/assets/styles/account.less index cd4730ca..d6f43a30 100644 --- a/client/assets/styles/account.less +++ b/client/assets/styles/account.less @@ -38,9 +38,10 @@ text-transform: uppercase; figure { - font-size: 125%; + width: 75%; display: flex; flex-flow: column; + margin-bottom: 1em; button { width: 100%; diff --git a/client/src/actions.jsx b/client/src/actions.jsx index 5a829cb7..d4242eb1 100644 --- a/client/src/actions.jsx +++ b/client/src/actions.jsx @@ -31,6 +31,7 @@ export const setShowLog = value => ({ type: 'SET_SHOW_LOG', value }); export const setShowNav = value => ({ type: 'SET_SHOW_NAV', value }); export const setSkip = value => ({ type: 'SET_SKIP', value }); export const setShop = value => ({ type: 'SET_SHOP', value }); +export const setSubscription = value => ({ type: 'SET_SUBSCRIPTION', value }); export const setTeam = value => ({ type: 'SET_TEAM', value: Array.from(value) }); export const setTeamPage = value => ({ type: 'SET_TEAM_PAGE', value }); diff --git a/client/src/components/account.management.jsx b/client/src/components/account.management.jsx index d20c52a1..ba645c5f 100644 --- a/client/src/components/account.management.jsx +++ b/client/src/components/account.management.jsx @@ -12,6 +12,7 @@ const addState = connect( function receiveState(state) { const { account, + subscription, email, ping, ws, @@ -55,6 +56,7 @@ const addState = connect( return { account, + subscription, ping, email, logout, @@ -81,6 +83,7 @@ class AccountStatus extends Component { render(args, state) { const { account, + subscription, ping, email, logout, @@ -128,15 +131,31 @@ class AccountStatus extends Component { return this.setState({ unsubState: false }); } + const subInfo = () => { + if (!subscription) return false; + return
+

Subscription

+
+
Period End
+
{new Date(subscription.current_period_end * 1000).toString()}
+
Status
+
{subscription.cancel_at_period_end ? 'Disabled' : 'Active'}
+
+
; + } + return (

{account.name}

-
-
Subscription
-
{account.subscribed ? 'Active' : 'Unsubscribed'}
-
- {unsubBtn()} +
+
+
spawn new construct
+ +
+
@@ -190,13 +209,9 @@ class AccountStatus extends Component { Set Password -
-
-
spawn new construct
- -
+
+ {subInfo()} + {unsubBtn()}
); diff --git a/client/src/components/account.status.jsx b/client/src/components/account.status.jsx index fce012cf..422d2233 100644 --- a/client/src/components/account.status.jsx +++ b/client/src/components/account.status.jsx @@ -13,11 +13,18 @@ function pingColour(ping) { const addState = connect( function receiveState(state) { const { + ws, account, ping, } = state; + function sendAccountStates() { + ws.sendEmailState(); + ws.sendSubscriptionState(); + } + return { + sendAccountStates, account, ping, }; @@ -49,10 +56,16 @@ function AccountStatus(args) { account, ping, accountPage, + sendAccountStates, } = args; if (!account) return null; + function accountClick() { + sendAccountStates(); + accountPage(); + } + return (
- +
); } diff --git a/client/src/events.jsx b/client/src/events.jsx index 04a4e6f6..dc00440b 100644 --- a/client/src/events.jsx +++ b/client/src/events.jsx @@ -19,6 +19,10 @@ function registerEvents(store) { setNav('play'); } + function setSubscription(sub) { + store.dispatch(actions.setSubscription(sub)); + } + function setConstructList(constructs) { store.dispatch(actions.setConstructs(constructs)); } @@ -227,6 +231,7 @@ function registerEvents(store) { setPing, setShop, setTeam, + setSubscription, setWs, notify, diff --git a/client/src/reducers.jsx b/client/src/reducers.jsx index 73c8e16c..35c45a64 100644 --- a/client/src/reducers.jsx +++ b/client/src/reducers.jsx @@ -44,6 +44,8 @@ module.exports = { skip: createReducer(false, 'SET_SKIP'), shop: createReducer(false, 'SET_SHOP'), + subscription: createReducer(null, 'SET_SUBSCRIPTION'), + team: createReducer([], 'SET_TEAM'), teamPage: createReducer(0, 'SET_TEAM_PAGE'), teamSelect: createReducer([null, null, null], 'SET_TEAM_SELECT'), diff --git a/client/src/socket.jsx b/client/src/socket.jsx index 02c1735d..e548ed95 100644 --- a/client/src/socket.jsx +++ b/client/src/socket.jsx @@ -143,6 +143,15 @@ function createSocket(events) { function sendMtxConstructSpawn() { send(['MtxConstructSpawn', {}]); } + + function sendEmailState() { + send(['EmailState', {}]); + } + + function sendSubscriptionState() { + send(['SubscriptionState', {}]); + } + // ------------- // Incoming // ------------- @@ -170,8 +179,9 @@ function createSocket(events) { events.setTeam(constructs); } - function onAccountSubscription() { - events.notify('Your account has been set to cancelled. You will no longer be billed. Thanks for playing.'); + function onSubscriptionState(sub) { + // events.subscriptionState(`Subscription cancelled. Your subscription will remain active until ${exp}. Thank you for your support.`); + events.setSubscription(sub); } function onConstructSpawn(construct) { @@ -208,7 +218,7 @@ function createSocket(events) { AccountTeam: onAccountTeam, AccountInstances: onAccountInstances, AccountShop: onAccountShop, - AccountSubscription: onAccountSubscription, + SubscriptionState: onSubscriptionState, ConstructSpawn: onConstructSpawn, GameState: onGameState, EmailState: onEmailState, @@ -326,6 +336,9 @@ function createSocket(events) { sendItemInfo, + sendEmailState, + sendSubscriptionState, + sendMtxApply, sendMtxBuy, sendMtxConstructSpawn, diff --git a/server/src/payments.rs b/server/src/payments.rs index 2961c1f8..798563fb 100644 --- a/server/src/payments.rs +++ b/server/src/payments.rs @@ -3,7 +3,7 @@ use std::io::Read; use http::State; use iron::prelude::*; -use iron::response::HttpResponse; + use iron::status; use persistent::Read as Readable; @@ -13,10 +13,10 @@ use postgres::transaction::Transaction; use failure::Error; use failure::err_msg; -use stripe::{Client, Event, EventObject, CheckoutSession, SubscriptionStatus}; +use stripe::{Client, Event, EventObject, CheckoutSession, SubscriptionStatus, Subscription}; use http::{MnmlHttpError}; -use pg::{PgPool}; +use pg::{Db, PgPool}; use account; use account::Account; @@ -36,7 +36,7 @@ pub fn subscription_account(tx: &mut Transaction, sub: String) -> Result Result, Error> { +pub fn subscription_cancel(tx: &mut Transaction, client: &Client, account: &Account) -> Result, Error> { let query = " SELECT account, customer, checkout, subscription FROM stripe_subscriptions @@ -57,19 +57,47 @@ pub fn subscription_cancel(tx: &mut Transaction, client: &Client, account: &Acco let mut params = stripe::UpdateSubscription::new(); params.cancel_at_period_end = Some(true); - let updated = match stripe::Subscription::update(client, &id, params) { - Ok(s) => s, + match stripe::Subscription::update(client, &id, params) { + Ok(s) => { + info!("subscription cancelled account={:?} subscription={:?}", account, s); + Ok(Some(s)) + }, Err(e) => { warn!("{:?}", e); - return Err(err_msg("unable to cancel subscription")); + Err(err_msg("unable to cancel subscription")) } - }; - - info!("subscription cancelled account={:?} subscription={:?}", account, updated); - - Ok(Some(updated.status.to_string())) + } } +pub fn account_subscription(db: &Db, client: &Client, account: &Account) -> Result, Error> { + let query = " + SELECT account, customer, checkout, subscription + FROM stripe_subscriptions + WHERE account = $1; + "; + + let result = db + .query(query, &[&account.id])?; + + let row = match result.iter().next() { + Some(r) => r, + None => return Ok(None), + }; + + let _customer: String = row.get(1); + let _checkout: String = row.get(2); + let subscription: String = row.get(3); + + let id = subscription.parse()?; + + match stripe::Subscription::retrieve(client, &id, &[]) { + Ok(s) => Ok(Some(s)), + Err(e) => { + warn!("{:?}", e); + Err(err_msg("unable to retrieve subscription")) + } + } +} // we use i64 because it is converted to BIGINT for pg // and we can losslessly pull it into u32 which is big diff --git a/server/src/rpc.rs b/server/src/rpc.rs index 13a4fa08..e4a1e0f3 100644 --- a/server/src/rpc.rs +++ b/server/src/rpc.rs @@ -11,7 +11,7 @@ use failure::err_msg; use serde_cbor::{from_slice, to_vec}; use cookie::Cookie; -use stripe::Client as StripeClient; +use stripe::{Client as StripeClient, Subscription}; use crossbeam_channel::{unbounded, Sender as CbSender}; use ws::{listen, CloseCode, Message, Handler, Request, Response}; @@ -40,14 +40,15 @@ pub enum RpcMessage { AccountTeam(Vec), AccountInstances(Vec), AccountShop(mtx::Shop), - AccountSubscription(Option), + ConstructSpawn(Construct), - EmailState(Email), GameState(Game), ItemInfo(ItemInfoCtr), InstanceState(Instance), + EmailState(Option), + SubscriptionState(Option), Pong(()), @@ -81,6 +82,8 @@ enum RpcRequest { AccountSetTeam { ids: Vec }, SubscriptionCancel {}, + SubscriptionState {}, + EmailState {}, InstanceQueue {}, InstancePractice {}, @@ -143,13 +146,19 @@ impl Connection { let response = match v { RpcRequest::AccountState {} => - return Ok(RpcMessage::AccountState(account.clone())), + Ok(RpcMessage::AccountState(account.clone())), RpcRequest::AccountConstructs {} => Ok(RpcMessage::AccountConstructs(account::constructs(&mut tx, &account)?)), RpcRequest::AccountSetTeam { ids } => Ok(RpcMessage::AccountTeam(account::set_team(&mut tx, &account, ids)?)), + RpcRequest::EmailState {} => + Ok(RpcMessage::EmailState(mail::select_account(&db, account.id)?)), + + RpcRequest::SubscriptionState {} => + Ok(RpcMessage::SubscriptionState(payments::account_subscription(&db, &self.stripe, &account)?)), + // RpcRequest::AccountShop {} => // Ok(RpcMessage::AccountShop(mtx::account_shop(&mut tx, &account)?)), @@ -201,7 +210,7 @@ impl Connection { Ok(RpcMessage::AccountShop(mtx::buy(&mut tx, account, mtx)?)), RpcRequest::SubscriptionCancel { } => - Ok(RpcMessage::AccountSubscription(payments::subscription_cancel(&mut tx, &self.stripe, account)?)), + Ok(RpcMessage::SubscriptionState(payments::subscription_cancel(&mut tx, &self.stripe, account)?)), _ => Err(format_err!("unknown request request={:?}", request)), }; @@ -239,15 +248,6 @@ impl Handler for Connection { let db = self.pool.get().unwrap(); let mut tx = db.transaction().unwrap(); - // email state - match mail::select_account(&db, a.id).unwrap() { - Some(e) => { - self.ws.send(RpcMessage::EmailState(e.clone())).unwrap(); - self.events.send(Event::Subscribe(self.id, e.id)).unwrap(); - }, - None => (), - }; - // send account constructs let account_constructs = account::constructs(&mut tx, a).unwrap(); self.ws.send(RpcMessage::AccountConstructs(account_constructs)).unwrap();