clean code
This commit is contained in:
parent
faafa73c6e
commit
98ee3a9c37
@ -116,7 +116,7 @@
|
|||||||
class="text-center mb-10 relative w-fit flex justify-center m-auto text-white"
|
class="text-center mb-10 relative w-fit flex justify-center m-auto text-white"
|
||||||
>
|
>
|
||||||
<div class="mb-4 flex justify-center -mt-3 lg:mb-3">
|
<div class="mb-4 flex justify-center -mt-3 lg:mb-3">
|
||||||
<a href="/learning-center/article/stocknear-discord-bot"
|
<a href="/discord-bot"
|
||||||
><div
|
><div
|
||||||
class="flex items-center justify-center sm:hover:text-muted dark:sm:hover:text-white text-blue-700 dark:text-blue-400"
|
class="flex items-center justify-center sm:hover:text-muted dark:sm:hover:text-white text-blue-700 dark:text-blue-400"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -2,32 +2,7 @@ import { error, fail, redirect } from "@sveltejs/kit";
|
|||||||
import { validateData } from "$lib/utils";
|
import { validateData } from "$lib/utils";
|
||||||
import { loginUserSchema, registerUserSchema } from "$lib/schemas";
|
import { loginUserSchema, registerUserSchema } from "$lib/schemas";
|
||||||
|
|
||||||
/*
|
|
||||||
export const load = async () => {
|
|
||||||
|
|
||||||
const apiKey = import.meta.env.VITE_LEMON_SQUEEZY_API_KEY;
|
|
||||||
|
|
||||||
const getLTDCount = async () => {
|
|
||||||
// make the POST request to the endpoint
|
|
||||||
const response = await fetch('https://api.lemonsqueezy.com/v1/order-items?page[size]=100', {
|
|
||||||
headers: {
|
|
||||||
'Accept': 'application/vnd.api+json',
|
|
||||||
'Content-Type': 'application/vnd.api+json',
|
|
||||||
'Authorization': `Bearer ${apiKey}`
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const output = await response.json();
|
|
||||||
const filteredData = output?.data?.filter(item => item?.attributes?.product_name === 'Pro Subscription (Life Time Access)');
|
|
||||||
const count = filteredData?.length || 0;
|
|
||||||
|
|
||||||
return count;
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
getLTDCount: await getLTDCount(),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
export const actions = {
|
export const actions = {
|
||||||
login: async ({ request, locals }) => {
|
login: async ({ request, locals }) => {
|
||||||
|
|||||||
@ -24,22 +24,14 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async function purchasePlan(subscriptionType: string = "") {
|
async function purchasePlan() {
|
||||||
if (data?.user) {
|
if (data?.user) {
|
||||||
let subId = "";
|
let subId = "";
|
||||||
|
|
||||||
if (subscriptionType === "lifeTime") {
|
if (mode) {
|
||||||
subId = import.meta.env.VITE_LEMON_SQUEEZY_LIFE_TIME_ACCESS_ID;
|
subId = import.meta.env.VITE_LEMON_SQUEEZY_DISCORD_ANNUAL_ID;
|
||||||
} else if (mode && subscriptionType === "plus") {
|
|
||||||
subId = import.meta.env.VITE_LEMON_SQUEEZY_ANNUAL_ID_PLUS;
|
|
||||||
} else if (!mode && subscriptionType === "plus") {
|
|
||||||
subId = import.meta.env.VITE_LEMON_SQUEEZY_MONTHLY_ID_PLUS;
|
|
||||||
} else if (mode && subscriptionType === "pro") {
|
|
||||||
subId = import.meta.env.VITE_LEMON_SQUEEZY_ANNUAL_ID_PRO;
|
|
||||||
} else if (!mode && subscriptionType === "pro") {
|
|
||||||
subId = import.meta.env.VITE_LEMON_SQUEEZY_MONTHLY_ID_PRO;
|
|
||||||
} else {
|
} else {
|
||||||
subId = import.meta.env.VITE_LEMON_SQUEEZY_ANNUAL_ID_PRO;
|
subId = import.meta.env.VITE_LEMON_SQUEEZY_DISCORD_MONTHLY_ID;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -66,8 +58,8 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SEO
|
<SEO
|
||||||
title="Pricing Plans"
|
title="Discord Bot Plans"
|
||||||
description="Get unlimited access to all of our data and tools, including full financial history, full ETF holdings, and more."
|
description="Explore our affordable Discord Bot plans. Unlock unlimited access and powerful features to supercharge your Discord server with Stocknear dataset."
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
@ -259,12 +251,13 @@
|
|||||||
<div
|
<div
|
||||||
class="flex flex-col sm:flex-row items-center justify-between w-full"
|
class="flex flex-col sm:flex-row items-center justify-between w-full"
|
||||||
>
|
>
|
||||||
<button
|
<label
|
||||||
on:click={() => purchasePlan("lifeTime")}
|
for={!data?.user ? "userLogin" : ""}
|
||||||
|
on:click={() => purchasePlan()}
|
||||||
class=" text-lg text-white cursor-pointer w-full lg:w-auto py-3 lg:mt-2 px-4 bg-blue-600 rounded font-semibold sm:hover:bg-blue-700 transition duration-100 flex items-center justify-center lg:justify-end"
|
class=" text-lg text-white cursor-pointer w-full lg:w-auto py-3 lg:mt-2 px-4 bg-blue-600 rounded font-semibold sm:hover:bg-blue-700 transition duration-100 flex items-center justify-center lg:justify-end"
|
||||||
>
|
>
|
||||||
Unlock Discord Bot
|
Unlock Discord Bot
|
||||||
</button>
|
</label>
|
||||||
<div
|
<div
|
||||||
class="hidden lg:flex flex-col justify-center lg:justify-end lg:ml-auto items-center mb-1"
|
class="hidden lg:flex flex-col justify-center lg:justify-end lg:ml-auto items-center mb-1"
|
||||||
>
|
>
|
||||||
@ -299,7 +292,7 @@
|
|||||||
class="relative overflow-hidden mt-4 card card-side mt-10 mb-10 bg-[#18181B] to-black rounded-lg p-5 flex flex-col lg:flex-row items-center justify-between"
|
class="relative overflow-hidden mt-4 card card-side mt-10 mb-10 bg-[#18181B] to-black rounded-lg p-5 flex flex-col lg:flex-row items-center justify-between"
|
||||||
>
|
>
|
||||||
<div class="card-body relative z-10 min-h-96 sm:min-h-0">
|
<div class="card-body relative z-10 min-h-96 sm:min-h-0">
|
||||||
<h2 class="card-title text-4xl font-bold mb-6 text-start">
|
<h2 class="card-title text-3xl sm:text-4xl font-bold mb-6 text-start">
|
||||||
See Our Discord Bots In Action!
|
See Our Discord Bots In Action!
|
||||||
</h2>
|
</h2>
|
||||||
<p
|
<p
|
||||||
@ -314,13 +307,15 @@
|
|||||||
href={discordURL}
|
href={discordURL}
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="btn btn-ghost bg-blue-600 text-lg font-bold px-5 py-2.5 rounded"
|
class="btn btn-ghost bg-blue-600 text-lg font-semibold px-5 py-3 rounded w-full sm:w-fit"
|
||||||
>
|
>
|
||||||
Join Discord
|
Join Discord
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<figure class="absolute -bottom-48 sm:top-5 sm:-right-20 z-2">
|
<figure
|
||||||
|
class="absolute -bottom-30 sm:-bottom-48 sm:top-5 sm:-right-20 z-2"
|
||||||
|
>
|
||||||
<div class="flex-shrink-0">
|
<div class="flex-shrink-0">
|
||||||
<img
|
<img
|
||||||
class="rounded-xl w-full sm:w-[600px] opacity-60 sm:opacity-100"
|
class="rounded-xl w-full sm:w-[600px] opacity-60 sm:opacity-100"
|
||||||
|
|||||||
117
src/routes/payment/discord/+server.ts
Normal file
117
src/routes/payment/discord/+server.ts
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import crypto from "node:crypto";
|
||||||
|
|
||||||
|
// Your secret key provided by Lemon Squeezy
|
||||||
|
const SECRET_KEY = import.meta.env.VITE_LEMON_SQUEEZY_SECRET_KEY;
|
||||||
|
|
||||||
|
if (!SECRET_KEY) {
|
||||||
|
throw new Error("Missing Lemon Squeezy secret key.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that the provided signature matches the HMAC digest for the given payload.
|
||||||
|
*
|
||||||
|
* @param {string} payload - The raw request body.
|
||||||
|
* @param {string} signatureHeader - The signature from the request header.
|
||||||
|
* @returns {boolean} - True if the signature is valid; otherwise, false.
|
||||||
|
*/
|
||||||
|
function isValidSignature(payload, signatureHeader) {
|
||||||
|
const hmac = crypto.createHmac("sha256", SECRET_KEY);
|
||||||
|
const computedDigestHex = hmac.update(payload).digest("hex");
|
||||||
|
|
||||||
|
// Convert both values to buffers for timing-safe comparison
|
||||||
|
const computedBuffer = Buffer.from(computedDigestHex, "utf8");
|
||||||
|
const signatureBuffer = Buffer.from(signatureHeader, "utf8");
|
||||||
|
|
||||||
|
// Ensure the buffers are the same length; if not, they can't be equal.
|
||||||
|
if (computedBuffer.length !== signatureBuffer.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return crypto.timingSafeEqual(computedBuffer, signatureBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const POST = async ({ request, locals }) => {
|
||||||
|
try {
|
||||||
|
const bodyText = await request.text();
|
||||||
|
|
||||||
|
// Retrieve the signature header; return early if missing.
|
||||||
|
const signatureHeader = request.headers.get("x-Signature");
|
||||||
|
if (!signatureHeader) {
|
||||||
|
console.error("Missing x-Signature header.");
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ error: "Missing signature header" }),
|
||||||
|
{
|
||||||
|
status: 403,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isValidSignature(bodyText, signatureHeader)) {
|
||||||
|
console.error("Signature verification failed.");
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ error: "Invalid signature" }),
|
||||||
|
{
|
||||||
|
status: 403,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the JSON payload
|
||||||
|
const payload = JSON.parse(bodyText);
|
||||||
|
const userId = payload?.meta?.custom_data?.userId;
|
||||||
|
const { status, refunded } = payload?.data?.attributes || {};
|
||||||
|
|
||||||
|
|
||||||
|
if (!userId || status === undefined) {
|
||||||
|
console.error("Missing userId or status in payload:", payload);
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ error: "Invalid payload structure" }),
|
||||||
|
{
|
||||||
|
status: 400,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
|
||||||
|
const paymentData = { user: userId, data: payload };
|
||||||
|
await locals.pb.collection("discordPayments").create(paymentData);
|
||||||
|
} catch (dbError) {
|
||||||
|
console.error("Database error:", dbError);
|
||||||
|
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ error: "Pocketbase error" }),
|
||||||
|
{
|
||||||
|
status: 500,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ message: "Payment data received successfully" }),
|
||||||
|
{
|
||||||
|
status: 200,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error processing request:", error);
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ error: "Internal server error" }),
|
||||||
|
{
|
||||||
|
status: 500,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user