Stripe integration flow
This commit is contained in:
@@ -28,18 +28,31 @@ export default class AuthHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getProfile(req, res) {
|
getRequestEmail(req, res) {
|
||||||
const token = req.cookies?.auth_token;
|
const token = req.cookies?.auth_token;
|
||||||
if (!token) return res.status(401).send({ error: "No auth token" });
|
if (!token) return res.status(401).send({ error: "No auth token" });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const payload = jwt.verify(token, process.env.JWT_SECRET);
|
const payload = jwt.verify(token, process.env.JWT_SECRET);
|
||||||
const email = payload.email;
|
const email = payload.email;
|
||||||
|
return email
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error getting profile: ", e)
|
||||||
|
throw new Error("Error getting email: invalid auth token")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getProfile(req, res) {
|
||||||
|
try {
|
||||||
|
let email = global.auth.getRequestEmail(req, res)
|
||||||
|
|
||||||
const user = db.members.getByEmail(email);
|
const user = db.members.getByEmail(email);
|
||||||
let connections = db.MEMBER_IN_NETWORK.getByMember(db.members.prefix + "-" + user.id)
|
let connections = db.MEMBER_IN_NETWORK.getByMember(db.members.prefix + "-" + user.id)
|
||||||
let userOrgs = connections.map((c) => {
|
let userOrgs = connections.map((c) => {
|
||||||
return db.networks.get(c.to)
|
let network = db.networks.get(c.to)
|
||||||
|
delete network.stripeAccountId
|
||||||
|
delete network.stripeAccessToken
|
||||||
|
return network
|
||||||
})
|
})
|
||||||
|
|
||||||
res.send({
|
res.send({
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ export default class Database {
|
|||||||
return model.indices[1] - model.indices[0] + 1
|
return model.indices[1] - model.indices[0] + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add a get in here that returns a safe copy, not the actual reference
|
||||||
|
|
||||||
addNode(prefix, node) {
|
addNode(prefix, node) {
|
||||||
try {
|
try {
|
||||||
let model = nodeModels[prefix]
|
let model = nodeModels[prefix]
|
||||||
@@ -76,7 +78,8 @@ export default class Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
editNode(prefix, id, toEdit) {
|
updateNode(prefix, id, toEdit) {
|
||||||
|
console.log("update node, ", toEdit)
|
||||||
try {
|
try {
|
||||||
let model = nodeModels[prefix]
|
let model = nodeModels[prefix]
|
||||||
if(model) {
|
if(model) {
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ export default class Post {
|
|||||||
postToEdit.edited = true;
|
postToEdit.edited = true;
|
||||||
|
|
||||||
let { authorId, ...rest } = postToEdit
|
let { authorId, ...rest } = postToEdit
|
||||||
global.db.editNode(this.prefix, id, rest)
|
global.db.updateNode(this.prefix, id, rest)
|
||||||
|
|
||||||
return postToEdit
|
return postToEdit
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
|
|||||||
@@ -43,15 +43,6 @@ export default class Member {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getByNetwork(id) {
|
|
||||||
let connections = db.MEMBER_IN_NETWORK.getByNetwork(id)
|
|
||||||
let members = []
|
|
||||||
connections.forEach((conn) => {
|
|
||||||
members.push(this.getByID(conn.from))
|
|
||||||
})
|
|
||||||
return members
|
|
||||||
}
|
|
||||||
|
|
||||||
async getPersonalData(id) {
|
async getPersonalData(id) {
|
||||||
const filePath = path.join(global.db.PERSONAL_DATA_PATH, id, "db.json");
|
const filePath = path.join(global.db.PERSONAL_DATA_PATH, id, "db.json");
|
||||||
|
|
||||||
@@ -63,6 +54,15 @@ export default class Member {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getByNetwork(id) {
|
||||||
|
let connections = db.MEMBER_IN_NETWORK.getByNetwork(id)
|
||||||
|
let members = []
|
||||||
|
connections.forEach((conn) => {
|
||||||
|
members.push(this.getByID(conn.from))
|
||||||
|
})
|
||||||
|
return members
|
||||||
|
}
|
||||||
|
|
||||||
getByID(id) {
|
getByID(id) {
|
||||||
if(typeof id === 'string') {
|
if(typeof id === 'string') {
|
||||||
id = id.split("-")[1]
|
id = id.split("-")[1]
|
||||||
|
|||||||
@@ -14,14 +14,18 @@ export default class Network {
|
|||||||
apps: z.array(z.string()),
|
apps: z.array(z.string()),
|
||||||
logo: z.string(),
|
logo: z.string(),
|
||||||
abbreviation: z.string(),
|
abbreviation: z.string(),
|
||||||
created: z.string()
|
created: z.string(),
|
||||||
|
stripeAccountId: z.string().nullable(),
|
||||||
|
stripeAccessToken: z.string().nullable()
|
||||||
})
|
})
|
||||||
.strict()
|
.strict()
|
||||||
|
|
||||||
get(stringID) {
|
get(id) {
|
||||||
let id = stringID.split("-")[1]
|
if(typeof id === "string") {
|
||||||
|
id = id.split("-")[1]
|
||||||
|
}
|
||||||
let index = this.indices[0] + (id - 1)
|
let index = this.indices[0] + (id - 1)
|
||||||
return global.db.nodes[index]
|
return structuredClone(global.db.nodes[index])
|
||||||
}
|
}
|
||||||
|
|
||||||
getByAbbreviation(abbreviation) {
|
getByAbbreviation(abbreviation) {
|
||||||
@@ -33,6 +37,21 @@ export default class Network {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update(id, data) {
|
||||||
|
if(data.id || data.created) {
|
||||||
|
throw new Error("Can't update node id or created time!")
|
||||||
|
}
|
||||||
|
let currentObject = this.get(id)
|
||||||
|
let newObject = {...currentObject, ...data}
|
||||||
|
|
||||||
|
try {
|
||||||
|
global.db.updateNode(this.prefix, newObject.id, newObject)
|
||||||
|
} catch(e) {
|
||||||
|
console.error(e)
|
||||||
|
throw new global.ServerError(400, "Failed to add member!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
save(n) {
|
save(n) {
|
||||||
let id = `${this.prefix}-${n.id}`
|
let id = `${this.prefix}-${n.id}`
|
||||||
let result = this.schema.safeParse(n)
|
let result = this.schema.safeParse(n)
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ class Server {
|
|||||||
/* Stripe */
|
/* Stripe */
|
||||||
router.post("/create-checkout-session", PaymentsHandler.newSubscription)
|
router.post("/create-checkout-session", PaymentsHandler.newSubscription)
|
||||||
router.post("/webhook", express.raw({ type: "application/json" }), PaymentsHandler.webhook)
|
router.post("/webhook", express.raw({ type: "application/json" }), PaymentsHandler.webhook)
|
||||||
|
router.post('/api/stripe/onboarded', PaymentsHandler.finishConnectSetup)
|
||||||
|
|
||||||
/* Auth */
|
/* Auth */
|
||||||
router.post('/login', this.auth.login)
|
router.post('/login', this.auth.login)
|
||||||
@@ -39,32 +40,10 @@ class Server {
|
|||||||
router.get('/db/images/*', this.getUserImage)
|
router.get('/db/images/*', this.getUserImage)
|
||||||
router.get('/api/orgdata/*', this.getOrgData)
|
router.get('/api/orgdata/*', this.getOrgData)
|
||||||
router.get('/api/mydata/*', this.getPersonalData)
|
router.get('/api/mydata/*', this.getPersonalData)
|
||||||
router.get('/api/stripe/onboarded', this.stripeOnboarded)
|
|
||||||
router.get('/*', this.get)
|
router.get('/*', this.get)
|
||||||
return router
|
return router
|
||||||
}
|
}
|
||||||
|
|
||||||
async stripeOnboarded() {
|
|
||||||
const { code } = req.body;
|
|
||||||
|
|
||||||
const response = await stripe.oauth.token({
|
|
||||||
grant_type: "authorization_code",
|
|
||||||
code,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { stripe_user_id, access_token } = response;
|
|
||||||
|
|
||||||
await db.users.update({
|
|
||||||
where: { id: req.user.id },
|
|
||||||
data: {
|
|
||||||
stripeAccountId: stripe_user_id,
|
|
||||||
stripeAccessToken: access_token,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
res.json({ success: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
getPersonalData = async (req, res, next) => {
|
getPersonalData = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const memberId = req.params[0]
|
const memberId = req.params[0]
|
||||||
@@ -243,8 +222,10 @@ class Server {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.db = new Database()
|
this.db = new Database()
|
||||||
global.db = this.db
|
|
||||||
this.auth = new AuthHandler()
|
this.auth = new AuthHandler()
|
||||||
|
global.db = this.db
|
||||||
|
global.auth = this.auth
|
||||||
|
global.payments = PaymentsHandler
|
||||||
const app = express();
|
const app = express();
|
||||||
app.post("/webhook", express.raw({ type: "application/json" }), PaymentsHandler.webhook)
|
app.post("/webhook", express.raw({ type: "application/json" }), PaymentsHandler.webhook)
|
||||||
const allowedOrigins = new Set([
|
const allowedOrigins = new Set([
|
||||||
|
|||||||
@@ -6,6 +6,56 @@ const stripe = new Stripe(process.env.STRIPE_SECRET);
|
|||||||
|
|
||||||
export default class PaymentsHandler {
|
export default class PaymentsHandler {
|
||||||
|
|
||||||
|
static async finishConnectSetup(req, res) {
|
||||||
|
const { code, networkId } = req.body;
|
||||||
|
console.log("onboarded", networkId)
|
||||||
|
|
||||||
|
const response = await stripe.oauth.token({
|
||||||
|
grant_type: "authorization_code",
|
||||||
|
code,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { stripe_user_id, access_token } = response;
|
||||||
|
|
||||||
|
await db.networks.update(
|
||||||
|
networkId,
|
||||||
|
{
|
||||||
|
stripeAccountId: stripe_user_id,
|
||||||
|
stripeAccessToken: access_token, // rarely used, long-term access token for the platform
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json({ success: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getProfile(networkId) {
|
||||||
|
let network = global.db.networks.get(networkId)
|
||||||
|
if (network) {
|
||||||
|
if (network.stripeAccountId) {
|
||||||
|
const account = await stripe.accounts.retrieve(network.stripeAccountId);
|
||||||
|
return {
|
||||||
|
chargesEnabled: account.charges_enabled,
|
||||||
|
payoutsEnabled: account.payouts_enabled,
|
||||||
|
detailsSubmitted: account.details_submitted,
|
||||||
|
email: account.email,
|
||||||
|
country: account.country,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return { connected: false }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(`Network ${networkId} not found`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getCustomers() {
|
||||||
|
const customers = await stripe.customers.list(
|
||||||
|
{ limit: 100 },
|
||||||
|
{ stripeAccount: 'acct_connected_account_id' }
|
||||||
|
);
|
||||||
|
console.log(customers)
|
||||||
|
}
|
||||||
|
|
||||||
static async newSubscription(req, res) {
|
static async newSubscription(req, res) {
|
||||||
try {
|
try {
|
||||||
const session = await stripe.checkout.sessions.create({
|
const session = await stripe.checkout.sessions.create({
|
||||||
|
|||||||
@@ -2,16 +2,30 @@ import { WebSocket, WebSocketServer } from 'ws';
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
|
|
||||||
import * as serverFunctions from "../../ui/_/code/bridge/serverFunctions.js"
|
import server from "../../ui/_/code/bridge/serverFunctions.js"
|
||||||
import ForumHandler from "./handlers/ForumHandler.js"
|
import ForumHandler from "./handlers/ForumHandler.js"
|
||||||
import MessagesHandler from "./handlers/MessagesHandler.js"
|
import MessagesHandler from "./handlers/MessagesHandler.js"
|
||||||
|
|
||||||
export default class Socket {
|
export default class Socket {
|
||||||
wss;
|
wss;
|
||||||
messageSchema = z.object({
|
|
||||||
|
functionCallSchema = z.object({
|
||||||
|
id: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
args: z.array(z.any()),
|
||||||
|
|
||||||
|
data: z.union([
|
||||||
|
z.object({}).passthrough(), // allows any object
|
||||||
|
z.array(z.any()) // allows any array
|
||||||
|
]).optional()
|
||||||
|
})
|
||||||
|
.strict()
|
||||||
|
|
||||||
|
appOperationSchema = z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
app: z.string().optional(),
|
app: z.string().optional(),
|
||||||
operation: z.string().optional(),
|
operation: z.string().optional(),
|
||||||
|
|
||||||
msg: z.union([
|
msg: z.union([
|
||||||
z.object({}).passthrough(), // allows any object
|
z.object({}).passthrough(), // allows any object
|
||||||
z.array(z.any()) // allows any array
|
z.array(z.any()) // allows any array
|
||||||
@@ -69,43 +83,64 @@ export default class Socket {
|
|||||||
// Build a system where the ws obj is updated every time on navigate, so it already has context
|
// Build a system where the ws obj is updated every time on navigate, so it already has context
|
||||||
// this way, we can only send broadcast messages to clients that actually have that app / subapp open
|
// this way, we can only send broadcast messages to clients that actually have that app / subapp open
|
||||||
handleMessage = async (msg, ws) => {
|
handleMessage = async (msg, ws) => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const text = msg.toString();
|
const text = msg.toString();
|
||||||
const req = JSON.parse(text);
|
const req = JSON.parse(text);
|
||||||
if(!this.messageSchema.safeParse(req).success) throw new Error("Socket.handleMessage: Incoming ws message has incorrect format!")
|
console.log(req)
|
||||||
|
|
||||||
let responseData;
|
if(this.appOperationSchema.safeParse(req).success) {
|
||||||
switch (req.app) {
|
this.handleAppOperation(req, ws)
|
||||||
case "FORUM":
|
} else if(this.functionCallSchema.safeParse(req).success) {
|
||||||
responseData = await ForumHandler.handle(req.operation, req.msg, ws)
|
this.handleFunction(req, ws)
|
||||||
break;
|
} else {
|
||||||
|
throw new Error("Socket.handleMessage: Incoming ws message has incorrect format!")
|
||||||
case "MESSAGES":
|
|
||||||
responseData = MessagesHandler.handle(req.operation, req.msg, ws)
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if(!req.app) {
|
|
||||||
let func = req.msg
|
|
||||||
responseData = serverFunctions[func.name](...args)
|
|
||||||
} else {
|
|
||||||
console.error("unknown ws message")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = {
|
|
||||||
...req
|
|
||||||
}
|
|
||||||
response.msg = responseData
|
|
||||||
|
|
||||||
if(!this.messageSchema.safeParse(response).success) throw new Error("Socket.handleMessage: Outgoing ws message has incorrect format!")
|
|
||||||
ws.send(JSON.stringify(response))
|
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Invalid WS message:", e);
|
console.error("Invalid WS message:", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async handleAppOperation(req, ws) {
|
||||||
|
let responseData;
|
||||||
|
switch (req.app) {
|
||||||
|
case "FORUM":
|
||||||
|
responseData = await ForumHandler.handle(req.operation, req.msg, ws)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "MESSAGES":
|
||||||
|
responseData = MessagesHandler.handle(req.operation, req.msg, ws)
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.log("unknown ws message")
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = {
|
||||||
|
...req
|
||||||
|
}
|
||||||
|
response.msg = responseData
|
||||||
|
|
||||||
|
if(!this.appOperationSchema.safeParse(response).success) throw new Error("Socket.handleMessage: Outgoing ws message has incorrect format!")
|
||||||
|
ws.send(JSON.stringify(response))
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleFunction(req, ws) {
|
||||||
|
console.log("func call: ", req.name, req.args)
|
||||||
|
let responseData = await server[req.name](...req.args)
|
||||||
|
|
||||||
|
let response = {
|
||||||
|
...req
|
||||||
|
}
|
||||||
|
response.data = responseData
|
||||||
|
|
||||||
|
console.log(response)
|
||||||
|
|
||||||
|
if(!this.functionCallSchema.safeParse(response).success) throw new Error("Socket.handleMessage: Outgoing ws message has incorrect format!")
|
||||||
|
ws.send(JSON.stringify(response))
|
||||||
|
}
|
||||||
|
|
||||||
broadcast(event) {
|
broadcast(event) {
|
||||||
if (!this.wss) return;
|
if (!this.wss) return;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const IS_NODE =
|
export const IS_NODE =
|
||||||
typeof process !== "undefined" &&
|
typeof process !== "undefined" &&
|
||||||
process.versions?.node != null
|
process.versions?.node != null
|
||||||
|
|
||||||
@@ -9,9 +9,7 @@ async function bridgeSend(name, args) {
|
|||||||
args: args
|
args: args
|
||||||
})
|
})
|
||||||
|
|
||||||
const json = await res.json()
|
return res
|
||||||
if (!res.ok) throw new Error(json.error)
|
|
||||||
return json.result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,8 +22,6 @@ export function createBridge(funcs) {
|
|||||||
get(target, prop) {
|
get(target, prop) {
|
||||||
const orig = target[prop]
|
const orig = target[prop]
|
||||||
|
|
||||||
if (typeof orig !== "function") return orig
|
|
||||||
|
|
||||||
return function (...args) {
|
return function (...args) {
|
||||||
if (IS_NODE) {
|
if (IS_NODE) {
|
||||||
return orig(...args)
|
return orig(...args)
|
||||||
|
|||||||
14
ui/_/code/bridge/handlers.js
Normal file
14
ui/_/code/bridge/handlers.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import fs from "fs"
|
||||||
|
|
||||||
|
const handlers = {
|
||||||
|
getProfile(one, two) {
|
||||||
|
fs.writeFileSync("output.txt", `${one} ${two}`)
|
||||||
|
return "written to disk"
|
||||||
|
},
|
||||||
|
|
||||||
|
async getStripeProfile(networkId) {
|
||||||
|
return global.payments.getProfile(networkId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default handlers
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
import fs from "fs"
|
import { createBridge, IS_NODE } from "./bridge.js"
|
||||||
import { createBridge } from "./bridge.js"
|
|
||||||
|
|
||||||
const handlers = {
|
let handlers = {}
|
||||||
getProfile(one, two) {
|
|
||||||
fs.writeFileSync("output.txt", `${one} ${two}`)
|
if (IS_NODE) {
|
||||||
return "written to disk"
|
const mod = await import("./handlers.js")
|
||||||
},
|
handlers = mod.default
|
||||||
}
|
}
|
||||||
|
|
||||||
export const { getProfile } = createBridge(handlers)
|
export default createBridge(handlers)
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
Sam Russell
|
Sam Russell
|
||||||
Captured Sun
|
Captured Sun
|
||||||
|
3.4.26 - Making horizontalAlign() and verticalAlign() methods of checking for stacks more robust
|
||||||
2.27.26 - Adding parentShadow() function
|
2.27.26 - Adding parentShadow() function
|
||||||
2.16.26 - Adding event objects to the onTouch callbacks
|
2.16.26 - Adding event objects to the onTouch callbacks
|
||||||
1.16.26 - Moving nav event dispatch out of pushState, adding null feature to attr()
|
1.16.26 - Moving nav event dispatch out of pushState, adding null feature to attr()
|
||||||
@@ -673,11 +674,11 @@ HTMLElement.prototype.centerY = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
HTMLElement.prototype.verticalAlign = function (value) {
|
HTMLElement.prototype.verticalAlign = function (value) {
|
||||||
const direction = getComputedStyle(this).flexDirection;
|
// if(!this.classList.contains("HStack") && !this.classList.contains("VStack")) {
|
||||||
if(!direction) {
|
// throw new Error("verticalAlign can be only be used on HStacks or VStacks!")
|
||||||
throw new Error("verticalAlign can be only be used on HStacks or VStacks!")
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
|
const direction = getComputedStyle(this).flexDirection;
|
||||||
if (direction === "column" || direction === "column-reverse") {
|
if (direction === "column" || direction === "column-reverse") {
|
||||||
this.style.justifyContent = value;
|
this.style.justifyContent = value;
|
||||||
} else {
|
} else {
|
||||||
@@ -687,11 +688,11 @@ HTMLElement.prototype.verticalAlign = function (value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
HTMLElement.prototype.horizontalAlign = function (value) {
|
HTMLElement.prototype.horizontalAlign = function (value) {
|
||||||
const direction = getComputedStyle(this).flexDirection;
|
if(!this.classList.contains("HStack") && !this.classList.contains("VStack")) {
|
||||||
if(!direction) {
|
|
||||||
throw new Error("horizontalAlign can be only be used on HStacks or VStacks!")
|
throw new Error("horizontalAlign can be only be used on HStacks or VStacks!")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const direction = getComputedStyle(this).flexDirection;
|
||||||
if (direction === "column" || direction === "column-reverse") {
|
if (direction === "column" || direction === "column-reverse") {
|
||||||
this.style.alignItems = value;
|
this.style.alignItems = value;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ class Connection {
|
|||||||
|
|
||||||
constructor(receiveCB) {
|
constructor(receiveCB) {
|
||||||
this.receiveCB = receiveCB;
|
this.receiveCB = receiveCB;
|
||||||
|
this.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
init = async () => {
|
init = async () => {
|
||||||
|
|||||||
@@ -1,13 +1,22 @@
|
|||||||
import env from "/_/code/env.js"
|
import env from "/_/code/env.js"
|
||||||
|
import server from "/_/code/bridge/serverFunctions.js"
|
||||||
|
import "../../components/LoadingCircleSmall.js"
|
||||||
|
|
||||||
class Settings extends Shadow {
|
class Settings extends Shadow {
|
||||||
|
stripeDetails = null
|
||||||
|
|
||||||
handleConnectStripe = () => {
|
handleConnectStripe = () => {
|
||||||
|
const state = btoa(JSON.stringify({
|
||||||
|
returnTo: window.location.href,
|
||||||
|
networkId: global.currentNetwork.id
|
||||||
|
}));
|
||||||
|
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
response_type: 'code',
|
response_type: 'code',
|
||||||
client_id: env.client_id,
|
client_id: env.client_id,
|
||||||
scope: 'read_write',
|
scope: 'read_write',
|
||||||
redirect_uri: `${env.baseURL}/stripe/onboardingcomplete`,
|
redirect_uri: `${env.baseURL}/stripe/onboardingcomplete`,
|
||||||
|
state,
|
||||||
});
|
});
|
||||||
|
|
||||||
window.location.href = `https://connect.stripe.com/oauth/authorize?${params}`;
|
window.location.href = `https://connect.stripe.com/oauth/authorize?${params}`;
|
||||||
@@ -21,22 +30,38 @@ class Settings extends Shadow {
|
|||||||
.marginLeft(5, pct)
|
.marginLeft(5, pct)
|
||||||
|
|
||||||
VStack(() => {
|
VStack(() => {
|
||||||
// HStack(() => {
|
HStack(() => {
|
||||||
// p("Stripe Integration")
|
p("Stripe Integration")
|
||||||
// button("Set up Stripe")
|
|
||||||
// .maxWidth(10, em)
|
if(this.stripeDetails && this.stripeDetails.data.email) {
|
||||||
// .onClick((done) => {
|
p("connected")
|
||||||
// this.handleConnectStripe()
|
} else if(this.stripeDetails && this.stripeDetails.data.connected === false) {
|
||||||
// })
|
button("Set up Stripe")
|
||||||
// })
|
.maxWidth(10, em)
|
||||||
// .gap(10, pct)
|
.onClick((done) => {
|
||||||
// .verticalAlign("center")
|
this.handleConnectStripe()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
LoadingCircleSmall()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.gap(10, pct)
|
||||||
|
.verticalAlign("center")
|
||||||
})
|
})
|
||||||
.gap(0.5, em)
|
.gap(0.5, em)
|
||||||
.paddingLeft(5, pct)
|
.paddingLeft(5, pct)
|
||||||
.marginTop(4, em)
|
.marginTop(4, em)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getStripeProfile() {
|
||||||
|
this.stripeDetails = await server.getStripeProfile(global.currentNetwork.id)
|
||||||
|
this.rerender()
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.getStripeProfile()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
register(Settings)
|
register(Settings)
|
||||||
@@ -6,9 +6,29 @@ class AppMenu extends Shadow {
|
|||||||
"Settings": {src: "settings-src", size: "1.7em"}
|
"Settings": {src: "settings-src", size: "1.7em"}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unselectedIconStyle(el) {
|
||||||
|
return el
|
||||||
|
.paddingBottom(5, px)
|
||||||
|
.borderBottom("")
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedIconStyle(el) {
|
||||||
|
return el
|
||||||
|
.paddingBottom(4, px)
|
||||||
|
.borderBottom("1px solid var(--accent)")
|
||||||
|
}
|
||||||
|
|
||||||
|
onAppChange() {
|
||||||
|
let icons = this.$$("img")
|
||||||
|
icons.forEach((icon) => {
|
||||||
|
icon.styles(this.unselectedIconStyle)
|
||||||
|
})
|
||||||
|
let selected = this.$(`img[app="${global.currentApp}"]`)
|
||||||
|
selected.styles(this.selectedIconStyle)
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
VStack(() => {
|
VStack(() => {
|
||||||
|
|
||||||
function cssVariable(value) {
|
function cssVariable(value) {
|
||||||
return getComputedStyle(document.documentElement)
|
return getComputedStyle(document.documentElement)
|
||||||
.getPropertyValue("--" + value)
|
.getPropertyValue("--" + value)
|
||||||
@@ -25,8 +45,7 @@ class AppMenu extends Shadow {
|
|||||||
img(cssVariable(this.images[app].src), this.images[app].size)
|
img(cssVariable(this.images[app].src), this.images[app].size)
|
||||||
.attr({app: app})
|
.attr({app: app})
|
||||||
.padding(0.3, em)
|
.padding(0.3, em)
|
||||||
.paddingBottom(currentApp === app ? 4 : 5, px)
|
.styles(currentApp === app ? this.selectedIconStyle : this.unselectedIconStyle)
|
||||||
.borderBottom(currentApp === app ? "1px solid var(--accent)" : "")
|
|
||||||
.onClick(function (done) {
|
.onClick(function (done) {
|
||||||
if(!done) {
|
if(!done) {
|
||||||
this.style.transform = "translateY(10%)"
|
this.style.transform = "translateY(10%)"
|
||||||
@@ -54,7 +73,7 @@ class AppMenu extends Shadow {
|
|||||||
})
|
})
|
||||||
.onEvent("appchange", () => {
|
.onEvent("appchange", () => {
|
||||||
// console.log("app hello?") // BUG: Quill is not acknowledging this event unless there is something else in the function body
|
// console.log("app hello?") // BUG: Quill is not acknowledging this event unless there is something else in the function body
|
||||||
this.rerender()
|
this.onAppChange()
|
||||||
})
|
})
|
||||||
.onEvent("networkchange", () => {
|
.onEvent("networkchange", () => {
|
||||||
// console.log(global.currentApp)
|
// console.log(global.currentApp)
|
||||||
|
|||||||
23
ui/desktop/components/LoadingCircleSmall.js
Normal file
23
ui/desktop/components/LoadingCircleSmall.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
class LoadingCircleSmall extends Shadow {
|
||||||
|
render() {
|
||||||
|
div()
|
||||||
|
.borderRadius(100, pct)
|
||||||
|
.width(1, em).height(1, em)
|
||||||
|
.backgroundColor("var(--accent")
|
||||||
|
.transition("transform 1.75s ease-in-out")
|
||||||
|
.onAppear(function () {
|
||||||
|
let growing = true;
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
if (growing) {
|
||||||
|
this.style.transform = "scale(1.5)";
|
||||||
|
} else {
|
||||||
|
this.style.transform = "scale(0.7)";
|
||||||
|
}
|
||||||
|
growing = !growing;
|
||||||
|
}, 750);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
register(LoadingCircleSmall)
|
||||||
@@ -27,23 +27,30 @@ let Global = class {
|
|||||||
return json
|
return json
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async handleStripeOnboarding() {
|
||||||
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
const code = params.get("code");
|
||||||
|
const { returnTo, networkId } = JSON.parse(atob(params.get("state")));
|
||||||
|
|
||||||
|
if (code) {
|
||||||
|
console.log("success!: ", code)
|
||||||
|
await fetch("/api/stripe/onboarded", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ code, networkId })
|
||||||
|
});
|
||||||
|
|
||||||
|
window.location.href = returnTo
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new Error("Stripe code is not present!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onNavigate = async () => {
|
onNavigate = async () => {
|
||||||
console.log("onnavigate", this.getFirstPathSegment())
|
console.log("onnavigate", this.getFirstPathSegment())
|
||||||
if(this.getFirstPathSegment() === "stripe") {
|
if(this.getFirstPathSegment() === "stripe") {
|
||||||
const params = new URLSearchParams(window.location.search);
|
this.handleStripeOnboarding()
|
||||||
const code = params.get("code");
|
|
||||||
|
|
||||||
if (code) {
|
|
||||||
console.log("success!: ", code)
|
|
||||||
await fetch("/api/stripe/connect", {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({ code })
|
|
||||||
});
|
|
||||||
|
|
||||||
} else {
|
|
||||||
throw new Error("Stripe code is not present!")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let selectedNetwork = this.networkFromPath()
|
let selectedNetwork = this.networkFromPath()
|
||||||
|
|||||||
Reference in New Issue
Block a user