email and sub status
This commit is contained in:
parent
d7b887e1ad
commit
ffc070ed51
@ -3,7 +3,9 @@
|
|||||||
*PRODUCTION*
|
*PRODUCTION*
|
||||||
* ACP
|
* ACP
|
||||||
* essential
|
* essential
|
||||||
* stripe verify
|
* serde serialize privatise
|
||||||
|
* DO postgres
|
||||||
|
* mobile styles
|
||||||
|
|
||||||
* treats
|
* treats
|
||||||
* constructs jiggle when clicked
|
* constructs jiggle when clicked
|
||||||
@ -11,12 +13,8 @@
|
|||||||
|
|
||||||
* bot game grind
|
* bot game grind
|
||||||
|
|
||||||
* serde serialize privatise
|
|
||||||
* mobile styles
|
|
||||||
* change score to enum
|
* change score to enum
|
||||||
* pct based translates for combat animation
|
* pct based translates for combat animation
|
||||||
* acp init
|
|
||||||
* DO postgres
|
|
||||||
* make our own toasts / msg pane
|
* make our own toasts / msg pane
|
||||||
* send account_instances on players update
|
* send account_instances on players update
|
||||||
|
|
||||||
|
|||||||
@ -38,9 +38,10 @@
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
|
||||||
figure {
|
figure {
|
||||||
font-size: 125%;
|
width: 75%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column;
|
flex-flow: column;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
|
||||||
button {
|
button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@ -31,6 +31,7 @@ export const setShowLog = value => ({ type: 'SET_SHOW_LOG', value });
|
|||||||
export const setShowNav = value => ({ type: 'SET_SHOW_NAV', value });
|
export const setShowNav = value => ({ type: 'SET_SHOW_NAV', value });
|
||||||
export const setSkip = value => ({ type: 'SET_SKIP', value });
|
export const setSkip = value => ({ type: 'SET_SKIP', value });
|
||||||
export const setShop = value => ({ type: 'SET_SHOP', 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 setTeam = value => ({ type: 'SET_TEAM', value: Array.from(value) });
|
||||||
export const setTeamPage = value => ({ type: 'SET_TEAM_PAGE', value });
|
export const setTeamPage = value => ({ type: 'SET_TEAM_PAGE', value });
|
||||||
|
|||||||
@ -12,6 +12,7 @@ const addState = connect(
|
|||||||
function receiveState(state) {
|
function receiveState(state) {
|
||||||
const {
|
const {
|
||||||
account,
|
account,
|
||||||
|
subscription,
|
||||||
email,
|
email,
|
||||||
ping,
|
ping,
|
||||||
ws,
|
ws,
|
||||||
@ -55,6 +56,7 @@ const addState = connect(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
account,
|
account,
|
||||||
|
subscription,
|
||||||
ping,
|
ping,
|
||||||
email,
|
email,
|
||||||
logout,
|
logout,
|
||||||
@ -81,6 +83,7 @@ class AccountStatus extends Component {
|
|||||||
render(args, state) {
|
render(args, state) {
|
||||||
const {
|
const {
|
||||||
account,
|
account,
|
||||||
|
subscription,
|
||||||
ping,
|
ping,
|
||||||
email,
|
email,
|
||||||
logout,
|
logout,
|
||||||
@ -128,15 +131,31 @@ class AccountStatus extends Component {
|
|||||||
return this.setState({ unsubState: false });
|
return this.setState({ unsubState: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const subInfo = () => {
|
||||||
|
if (!subscription) return false;
|
||||||
|
return <div>
|
||||||
|
<h3>Subscription</h3>
|
||||||
|
<dl>
|
||||||
|
<dt>Period End</dt>
|
||||||
|
<dd>{new Date(subscription.current_period_end * 1000).toString()}</dd>
|
||||||
|
<dt>Status</dt>
|
||||||
|
<dd>{subscription.cancel_at_period_end ? 'Disabled' : 'Active'}</dd>
|
||||||
|
</dl>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section class='account' onClick={tlClick}>
|
<section class='account' onClick={tlClick}>
|
||||||
<div>
|
<div>
|
||||||
<h1>{account.name}</h1>
|
<h1>{account.name}</h1>
|
||||||
<dl>
|
<div class="list">
|
||||||
<dt>Subscription</dt>
|
<figure>
|
||||||
<dd>{account.subscribed ? 'Active' : 'Unsubscribed'}</dd>
|
<figcaption>spawn new construct</figcaption>
|
||||||
</dl>
|
<button onClick={() => sendConstructSpawn()} type="submit">
|
||||||
{unsubBtn()}
|
¤50
|
||||||
|
</button>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
<button onClick={() => logout()}>Logout</button>
|
<button onClick={() => logout()}>Logout</button>
|
||||||
<button><a href={`mailto:humans@mnml.gg?subject=Account%20Support:%20${account.name}`}>✉ support</a></button>
|
<button><a href={`mailto:humans@mnml.gg?subject=Account%20Support:%20${account.name}`}>✉ support</a></button>
|
||||||
</div>
|
</div>
|
||||||
@ -190,13 +209,9 @@ class AccountStatus extends Component {
|
|||||||
Set Password
|
Set Password
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="list">
|
<div>
|
||||||
<figure>
|
{subInfo()}
|
||||||
<figcaption>spawn new construct</figcaption>
|
{unsubBtn()}
|
||||||
<button onClick={() => sendConstructSpawn()} type="submit">
|
|
||||||
¤50
|
|
||||||
</button>
|
|
||||||
</figure>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -13,11 +13,18 @@ function pingColour(ping) {
|
|||||||
const addState = connect(
|
const addState = connect(
|
||||||
function receiveState(state) {
|
function receiveState(state) {
|
||||||
const {
|
const {
|
||||||
|
ws,
|
||||||
account,
|
account,
|
||||||
ping,
|
ping,
|
||||||
} = state;
|
} = state;
|
||||||
|
|
||||||
|
function sendAccountStates() {
|
||||||
|
ws.sendEmailState();
|
||||||
|
ws.sendSubscriptionState();
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
sendAccountStates,
|
||||||
account,
|
account,
|
||||||
ping,
|
ping,
|
||||||
};
|
};
|
||||||
@ -49,10 +56,16 @@ function AccountStatus(args) {
|
|||||||
account,
|
account,
|
||||||
ping,
|
ping,
|
||||||
accountPage,
|
accountPage,
|
||||||
|
sendAccountStates,
|
||||||
} = args;
|
} = args;
|
||||||
|
|
||||||
if (!account) return null;
|
if (!account) return null;
|
||||||
|
|
||||||
|
function accountClick() {
|
||||||
|
sendAccountStates();
|
||||||
|
accountPage();
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="account-status">
|
<div class="account-status">
|
||||||
<div class="account-info">
|
<div class="account-info">
|
||||||
@ -61,7 +74,7 @@ function AccountStatus(args) {
|
|||||||
<div class="ping-text">{ping}ms</div>
|
<div class="ping-text">{ping}ms</div>
|
||||||
</div>
|
</div>
|
||||||
<h3 class="account-header credits">{`¤${account.balance}`}</h3>
|
<h3 class="account-header credits">{`¤${account.balance}`}</h3>
|
||||||
<button onClick={() => accountPage()}>⚙ account</button>
|
<button onClick={accountClick}>⚙ account</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,10 @@ function registerEvents(store) {
|
|||||||
setNav('play');
|
setNav('play');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setSubscription(sub) {
|
||||||
|
store.dispatch(actions.setSubscription(sub));
|
||||||
|
}
|
||||||
|
|
||||||
function setConstructList(constructs) {
|
function setConstructList(constructs) {
|
||||||
store.dispatch(actions.setConstructs(constructs));
|
store.dispatch(actions.setConstructs(constructs));
|
||||||
}
|
}
|
||||||
@ -227,6 +231,7 @@ function registerEvents(store) {
|
|||||||
setPing,
|
setPing,
|
||||||
setShop,
|
setShop,
|
||||||
setTeam,
|
setTeam,
|
||||||
|
setSubscription,
|
||||||
setWs,
|
setWs,
|
||||||
|
|
||||||
notify,
|
notify,
|
||||||
|
|||||||
@ -44,6 +44,8 @@ module.exports = {
|
|||||||
skip: createReducer(false, 'SET_SKIP'),
|
skip: createReducer(false, 'SET_SKIP'),
|
||||||
shop: createReducer(false, 'SET_SHOP'),
|
shop: createReducer(false, 'SET_SHOP'),
|
||||||
|
|
||||||
|
subscription: createReducer(null, 'SET_SUBSCRIPTION'),
|
||||||
|
|
||||||
team: createReducer([], 'SET_TEAM'),
|
team: createReducer([], 'SET_TEAM'),
|
||||||
teamPage: createReducer(0, 'SET_TEAM_PAGE'),
|
teamPage: createReducer(0, 'SET_TEAM_PAGE'),
|
||||||
teamSelect: createReducer([null, null, null], 'SET_TEAM_SELECT'),
|
teamSelect: createReducer([null, null, null], 'SET_TEAM_SELECT'),
|
||||||
|
|||||||
@ -143,6 +143,15 @@ function createSocket(events) {
|
|||||||
function sendMtxConstructSpawn() {
|
function sendMtxConstructSpawn() {
|
||||||
send(['MtxConstructSpawn', {}]);
|
send(['MtxConstructSpawn', {}]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sendEmailState() {
|
||||||
|
send(['EmailState', {}]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendSubscriptionState() {
|
||||||
|
send(['SubscriptionState', {}]);
|
||||||
|
}
|
||||||
|
|
||||||
// -------------
|
// -------------
|
||||||
// Incoming
|
// Incoming
|
||||||
// -------------
|
// -------------
|
||||||
@ -170,8 +179,9 @@ function createSocket(events) {
|
|||||||
events.setTeam(constructs);
|
events.setTeam(constructs);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onAccountSubscription() {
|
function onSubscriptionState(sub) {
|
||||||
events.notify('Your account has been set to cancelled. You will no longer be billed. Thanks for playing.');
|
// events.subscriptionState(`Subscription cancelled. Your subscription will remain active until ${exp}. Thank you for your support.`);
|
||||||
|
events.setSubscription(sub);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onConstructSpawn(construct) {
|
function onConstructSpawn(construct) {
|
||||||
@ -208,7 +218,7 @@ function createSocket(events) {
|
|||||||
AccountTeam: onAccountTeam,
|
AccountTeam: onAccountTeam,
|
||||||
AccountInstances: onAccountInstances,
|
AccountInstances: onAccountInstances,
|
||||||
AccountShop: onAccountShop,
|
AccountShop: onAccountShop,
|
||||||
AccountSubscription: onAccountSubscription,
|
SubscriptionState: onSubscriptionState,
|
||||||
ConstructSpawn: onConstructSpawn,
|
ConstructSpawn: onConstructSpawn,
|
||||||
GameState: onGameState,
|
GameState: onGameState,
|
||||||
EmailState: onEmailState,
|
EmailState: onEmailState,
|
||||||
@ -326,6 +336,9 @@ function createSocket(events) {
|
|||||||
|
|
||||||
sendItemInfo,
|
sendItemInfo,
|
||||||
|
|
||||||
|
sendEmailState,
|
||||||
|
sendSubscriptionState,
|
||||||
|
|
||||||
sendMtxApply,
|
sendMtxApply,
|
||||||
sendMtxBuy,
|
sendMtxBuy,
|
||||||
sendMtxConstructSpawn,
|
sendMtxConstructSpawn,
|
||||||
|
|||||||
@ -3,7 +3,7 @@ use std::io::Read;
|
|||||||
|
|
||||||
use http::State;
|
use http::State;
|
||||||
use iron::prelude::*;
|
use iron::prelude::*;
|
||||||
use iron::response::HttpResponse;
|
|
||||||
use iron::status;
|
use iron::status;
|
||||||
use persistent::Read as Readable;
|
use persistent::Read as Readable;
|
||||||
|
|
||||||
@ -13,10 +13,10 @@ use postgres::transaction::Transaction;
|
|||||||
use failure::Error;
|
use failure::Error;
|
||||||
use failure::err_msg;
|
use failure::err_msg;
|
||||||
|
|
||||||
use stripe::{Client, Event, EventObject, CheckoutSession, SubscriptionStatus};
|
use stripe::{Client, Event, EventObject, CheckoutSession, SubscriptionStatus, Subscription};
|
||||||
|
|
||||||
use http::{MnmlHttpError};
|
use http::{MnmlHttpError};
|
||||||
use pg::{PgPool};
|
use pg::{Db, PgPool};
|
||||||
use account;
|
use account;
|
||||||
use account::Account;
|
use account::Account;
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ pub fn subscription_account(tx: &mut Transaction, sub: String) -> Result<Uuid, E
|
|||||||
Ok(row.get(0))
|
Ok(row.get(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subscription_cancel(tx: &mut Transaction, client: &Client, account: &Account) -> Result<Option<String>, Error> {
|
pub fn subscription_cancel(tx: &mut Transaction, client: &Client, account: &Account) -> Result<Option<Subscription>, Error> {
|
||||||
let query = "
|
let query = "
|
||||||
SELECT account, customer, checkout, subscription
|
SELECT account, customer, checkout, subscription
|
||||||
FROM stripe_subscriptions
|
FROM stripe_subscriptions
|
||||||
@ -57,19 +57,47 @@ pub fn subscription_cancel(tx: &mut Transaction, client: &Client, account: &Acco
|
|||||||
let mut params = stripe::UpdateSubscription::new();
|
let mut params = stripe::UpdateSubscription::new();
|
||||||
params.cancel_at_period_end = Some(true);
|
params.cancel_at_period_end = Some(true);
|
||||||
|
|
||||||
let updated = match stripe::Subscription::update(client, &id, params) {
|
match stripe::Subscription::update(client, &id, params) {
|
||||||
Ok(s) => s,
|
Ok(s) => {
|
||||||
|
info!("subscription cancelled account={:?} subscription={:?}", account, s);
|
||||||
|
Ok(Some(s))
|
||||||
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("{:?}", 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<Option<Subscription>, 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
|
// we use i64 because it is converted to BIGINT for pg
|
||||||
// and we can losslessly pull it into u32 which is big
|
// and we can losslessly pull it into u32 which is big
|
||||||
|
|||||||
@ -11,7 +11,7 @@ use failure::err_msg;
|
|||||||
use serde_cbor::{from_slice, to_vec};
|
use serde_cbor::{from_slice, to_vec};
|
||||||
use cookie::Cookie;
|
use cookie::Cookie;
|
||||||
|
|
||||||
use stripe::Client as StripeClient;
|
use stripe::{Client as StripeClient, Subscription};
|
||||||
|
|
||||||
use crossbeam_channel::{unbounded, Sender as CbSender};
|
use crossbeam_channel::{unbounded, Sender as CbSender};
|
||||||
use ws::{listen, CloseCode, Message, Handler, Request, Response};
|
use ws::{listen, CloseCode, Message, Handler, Request, Response};
|
||||||
@ -40,14 +40,15 @@ pub enum RpcMessage {
|
|||||||
AccountTeam(Vec<Construct>),
|
AccountTeam(Vec<Construct>),
|
||||||
AccountInstances(Vec<Instance>),
|
AccountInstances(Vec<Instance>),
|
||||||
AccountShop(mtx::Shop),
|
AccountShop(mtx::Shop),
|
||||||
AccountSubscription(Option<String>),
|
|
||||||
ConstructSpawn(Construct),
|
ConstructSpawn(Construct),
|
||||||
EmailState(Email),
|
|
||||||
GameState(Game),
|
GameState(Game),
|
||||||
ItemInfo(ItemInfoCtr),
|
ItemInfo(ItemInfoCtr),
|
||||||
|
|
||||||
InstanceState(Instance),
|
InstanceState(Instance),
|
||||||
|
|
||||||
|
EmailState(Option<Email>),
|
||||||
|
SubscriptionState(Option<Subscription>),
|
||||||
|
|
||||||
Pong(()),
|
Pong(()),
|
||||||
|
|
||||||
@ -81,6 +82,8 @@ enum RpcRequest {
|
|||||||
AccountSetTeam { ids: Vec<Uuid> },
|
AccountSetTeam { ids: Vec<Uuid> },
|
||||||
|
|
||||||
SubscriptionCancel {},
|
SubscriptionCancel {},
|
||||||
|
SubscriptionState {},
|
||||||
|
EmailState {},
|
||||||
|
|
||||||
InstanceQueue {},
|
InstanceQueue {},
|
||||||
InstancePractice {},
|
InstancePractice {},
|
||||||
@ -143,13 +146,19 @@ impl Connection {
|
|||||||
|
|
||||||
let response = match v {
|
let response = match v {
|
||||||
RpcRequest::AccountState {} =>
|
RpcRequest::AccountState {} =>
|
||||||
return Ok(RpcMessage::AccountState(account.clone())),
|
Ok(RpcMessage::AccountState(account.clone())),
|
||||||
RpcRequest::AccountConstructs {} =>
|
RpcRequest::AccountConstructs {} =>
|
||||||
Ok(RpcMessage::AccountConstructs(account::constructs(&mut tx, &account)?)),
|
Ok(RpcMessage::AccountConstructs(account::constructs(&mut tx, &account)?)),
|
||||||
|
|
||||||
RpcRequest::AccountSetTeam { ids } =>
|
RpcRequest::AccountSetTeam { ids } =>
|
||||||
Ok(RpcMessage::AccountTeam(account::set_team(&mut tx, &account, 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 {} =>
|
// RpcRequest::AccountShop {} =>
|
||||||
// Ok(RpcMessage::AccountShop(mtx::account_shop(&mut tx, &account)?)),
|
// Ok(RpcMessage::AccountShop(mtx::account_shop(&mut tx, &account)?)),
|
||||||
|
|
||||||
@ -201,7 +210,7 @@ impl Connection {
|
|||||||
Ok(RpcMessage::AccountShop(mtx::buy(&mut tx, account, mtx)?)),
|
Ok(RpcMessage::AccountShop(mtx::buy(&mut tx, account, mtx)?)),
|
||||||
|
|
||||||
RpcRequest::SubscriptionCancel { } =>
|
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)),
|
_ => Err(format_err!("unknown request request={:?}", request)),
|
||||||
};
|
};
|
||||||
@ -239,15 +248,6 @@ impl Handler for Connection {
|
|||||||
let db = self.pool.get().unwrap();
|
let db = self.pool.get().unwrap();
|
||||||
let mut tx = db.transaction().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
|
// send account constructs
|
||||||
let account_constructs = account::constructs(&mut tx, a).unwrap();
|
let account_constructs = account::constructs(&mut tx, a).unwrap();
|
||||||
self.ws.send(RpcMessage::AccountConstructs(account_constructs)).unwrap();
|
self.ws.send(RpcMessage::AccountConstructs(account_constructs)).unwrap();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user