aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2025-07-17 16:16:56 -0700
committerAdam Soltys <[email protected]>2025-07-17 16:16:56 -0700
commit8fae698d9ce0f318481ab739a63ea0a580372f86 (patch)
treef8be08e1f3c72d39087ed753811f7655787c7dc7
parent161678b774cee184fdb5afcf84686639acb7a5e2 (diff)
downloadcoinos-server-8fae698d9ce0f318481ab739a63ea0a580372f86.tar.gz
coinos-server-8fae698d9ce0f318481ab739a63ea0a580372f86.zip
square endpoints
-rwxr-xr-xbun.lockbbin209762 -> 232678 bytes
-rw-r--r--index.ts6
-rw-r--r--package.json2
-rw-r--r--routes/invoices.ts1
-rw-r--r--routes/square.ts89
5 files changed, 96 insertions, 2 deletions
diff --git a/bun.lockb b/bun.lockb
index 480a5a31..a35942bc 100755
--- a/bun.lockb
+++ b/bun.lockb
Binary files differ
diff --git a/index.ts b/index.ts
index 8c8d4eaf..fa2f9366 100644
--- a/index.ts
+++ b/index.ts
@@ -19,6 +19,7 @@ import nostr from "$routes/nostr";
import payments from "$routes/payments";
import rates from "$routes/rates";
import shopify from "$routes/shopify";
+import square from "$routes/square";
import users from "$routes/users";
try {
@@ -82,8 +83,9 @@ app.post("/replace", auth, payments.replace);
app.get("/decode/:bolt11", payments.decode);
app.post("/fetchinvoice", payments.fetchinvoice);
-app.get("/auth", payments.auth);
-app.post("/order", payments.order);
+app.get("/square/connect", auth, square.connect);
+app.get("/square/auth", auth, square.auth);
+app.post("/square/payment", square.payment);
app.get("/encode", lnurl.encode);
app.get("/decode", lnurl.decode);
diff --git a/package.json b/package.json
index 8881ff03..9e6de648 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
"@fastify/static": "^6.5.0",
"@fastify/websocket": "^10.0.1",
"@noble/hashes": "^1.4.0",
+ "@scure/base": "^1.2.6",
"bech32": "^2.0.0",
"buffer-crc32": "^0.2.13",
"clightning-client": "github:asoltys/lightning-client-js#master",
@@ -45,6 +46,7 @@
"readable-stream-clone": "^0.0.7",
"redis": "^4.6.10",
"sharp": "^0.33.5",
+ "square": "^43.0.0",
"strfry-rpc": "^0.0.1",
"uuid": "8",
"web-push": "^3.6.7",
diff --git a/routes/invoices.ts b/routes/invoices.ts
index ecac713c..6c2f713d 100644
--- a/routes/invoices.ts
+++ b/routes/invoices.ts
@@ -28,6 +28,7 @@ export default {
if (body.user) user = body.user;
if (req.user.username === user.username) invoice.own = true;
+ else invoice.own = false;
try {
const result = await generate({ invoice, user });
diff --git a/routes/square.ts b/routes/square.ts
new file mode 100644
index 00000000..f7902e11
--- /dev/null
+++ b/routes/square.ts
@@ -0,0 +1,89 @@
+import * as crypto from "crypto";
+import config from "$config";
+import { db, g, s } from "$lib/db";
+import { generate } from "$lib/invoices";
+import { SATS, bail, getUser } from "$lib/utils";
+import { sha256 } from "@noble/hashes/sha256";
+import { utf8ToBytes } from "@noble/hashes/utils";
+import { base64urlnopad as base64 } from "@scure/base";
+import { SquareClient } from "square";
+
+const { scopes, url, appId, environment } = config.square;
+
+export default {
+ async connect(req, res) {
+ const { user } = req;
+
+ if (await db.exists(`${user.id}:square`)) {
+ return res.send("connected");
+ }
+
+ const codeVerifier = base64.encode(crypto.randomBytes(32));
+ await s(`${user.id}:codeVerifier`, codeVerifier);
+
+ const challenge = base64.encode(sha256(utf8ToBytes(codeVerifier)));
+
+ const state = base64.encode(crypto.randomBytes(12));
+ const scope = scopes.join("+");
+
+ res.send(
+ `${url}oauth2/authorize?client_id=${appId}&session=false&scope=${scope}&state=${state}&code_challenge=${challenge}`,
+ );
+ },
+
+ async auth(req, res) {
+ const { user } = req;
+
+ const codeVerifier = await g(`${user.id}:codeVerifier`);
+
+ const client = new SquareClient({
+ environment,
+ });
+
+ const { code } = req.query;
+
+ try {
+ const result = await client.oAuth.obtainToken({
+ code,
+ clientId: appId,
+ grantType: "authorization_code",
+ codeVerifier,
+ });
+
+ await s(`${user.id}:square`, result);
+ await s(result.merchantId, user.id);
+ res.send({});
+ } catch (e) {
+ console.log(e);
+ }
+ },
+
+ async payment(req, res) {
+ try {
+ const { body } = req;
+ const { data, type, merchant_id } = body;
+ const { payment } = data.object;
+
+ if (type === "payment.created" && payment.source_type === "CASH") {
+ const {
+ amount_money: { amount, currency },
+ } = payment;
+ const rates = await g("rates");
+ const rate = rates[currency];
+ const uid = await g(merchant_id);
+ const user = await getUser(uid);
+ const invoice = {
+ amount: Math.round(((amount / 100) * SATS) / rate),
+ own: true,
+ type: "lightning",
+ };
+
+ await generate({ invoice, user });
+ }
+
+ res.send({});
+ } catch (e) {
+ bail(res, e.message);
+ }
+ },
+};