Skip to content

Commit

Permalink
custom discount code
Browse files Browse the repository at this point in the history
  • Loading branch information
GraemeFulton committed May 13, 2024
1 parent a20499a commit 6d60a7c
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 31 deletions.
161 changes: 136 additions & 25 deletions components/Sponsor/CheckoutTotal.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,42 @@
import { useEffect, useState } from "react";
import PurchaseButton from "./PurchaseButton";
import axios from "axios";

export const HandHeartIcon = ({ className }) => (
<svg className={className} xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256"><path d="M230.33,141.06a24.34,24.34,0,0,0-18.61-4.77C230.5,117.33,240,98.48,240,80c0-26.47-21.29-48-47.46-48A47.58,47.58,0,0,0,156,48.75,47.58,47.58,0,0,0,119.46,32C93.29,32,72,53.53,72,80c0,11,3.24,21.69,10.06,33a31.87,31.87,0,0,0-14.75,8.4L44.69,144H16A16,16,0,0,0,0,160v40a16,16,0,0,0,16,16H120a7.93,7.93,0,0,0,1.94-.24l64-16a6.94,6.94,0,0,0,1.19-.4L226,182.82l.44-.2a24.6,24.6,0,0,0,3.93-41.56ZM119.46,48A31.15,31.15,0,0,1,148.6,67a8,8,0,0,0,14.8,0,31.15,31.15,0,0,1,29.14-19C209.59,48,224,62.65,224,80c0,19.51-15.79,41.58-45.66,63.9l-11.09,2.55A28,28,0,0,0,140,112H100.68C92.05,100.36,88,90.12,88,80,88,62.65,102.41,48,119.46,48ZM16,160H40v40H16Zm203.43,8.21-38,16.18L119,200H56V155.31l22.63-22.62A15.86,15.86,0,0,1,89.94,128H140a12,12,0,0,1,0,24H112a8,8,0,0,0,0,16h32a8.32,8.32,0,0,0,1.79-.2l67-15.41.31-.08a8.6,8.6,0,0,1,6.3,15.9Z"></path></svg>
)
<svg
className={className}
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
fill="currentColor"
viewBox="0 0 256 256"
>
<path d="M230.33,141.06a24.34,24.34,0,0,0-18.61-4.77C230.5,117.33,240,98.48,240,80c0-26.47-21.29-48-47.46-48A47.58,47.58,0,0,0,156,48.75,47.58,47.58,0,0,0,119.46,32C93.29,32,72,53.53,72,80c0,11,3.24,21.69,10.06,33a31.87,31.87,0,0,0-14.75,8.4L44.69,144H16A16,16,0,0,0,0,160v40a16,16,0,0,0,16,16H120a7.93,7.93,0,0,0,1.94-.24l64-16a6.94,6.94,0,0,0,1.19-.4L226,182.82l.44-.2a24.6,24.6,0,0,0,3.93-41.56ZM119.46,48A31.15,31.15,0,0,1,148.6,67a8,8,0,0,0,14.8,0,31.15,31.15,0,0,1,29.14-19C209.59,48,224,62.65,224,80c0,19.51-15.79,41.58-45.66,63.9l-11.09,2.55A28,28,0,0,0,140,112H100.68C92.05,100.36,88,90.12,88,80,88,62.65,102.41,48,119.46,48ZM16,160H40v40H16Zm203.43,8.21-38,16.18L119,200H56V155.31l22.63-22.62A15.86,15.86,0,0,1,89.94,128H140a12,12,0,0,1,0,24H112a8,8,0,0,0,0,16h32a8.32,8.32,0,0,0,1.79-.2l67-15.41.31-.08a8.6,8.6,0,0,1,6.3,15.9Z"></path>
</svg>
);
export const CoinsIcon = ({ className }) => (
<svg className={className} xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256"><path d="M184,89.57V84c0-25.08-37.83-44-88-44S8,58.92,8,84v40c0,20.89,26.25,37.49,64,42.46V172c0,25.08,37.83,44,88,44s88-18.92,88-44V132C248,111.3,222.58,94.68,184,89.57ZM232,132c0,13.22-30.79,28-72,28-3.73,0-7.43-.13-11.08-.37C170.49,151.77,184,139,184,124V105.74C213.87,110.19,232,122.27,232,132ZM72,150.25V126.46A183.74,183.74,0,0,0,96,128a183.74,183.74,0,0,0,24-1.54v23.79A163,163,0,0,1,96,152,163,163,0,0,1,72,150.25Zm96-40.32V124c0,8.39-12.41,17.4-32,22.87V123.5C148.91,120.37,159.84,115.71,168,109.93ZM96,56c41.21,0,72,14.78,72,28s-30.79,28-72,28S24,97.22,24,84,54.79,56,96,56ZM24,124V109.93c8.16,5.78,19.09,10.44,32,13.57v23.37C36.41,141.4,24,132.39,24,124Zm64,48v-4.17c2.63.1,5.29.17,8,.17,3.88,0,7.67-.13,11.39-.35A121.92,121.92,0,0,0,120,171.41v23.46C100.41,189.4,88,180.39,88,172Zm48,26.25V174.4a179.48,179.48,0,0,0,24,1.6,183.74,183.74,0,0,0,24-1.54v23.79a165.45,165.45,0,0,1-48,0Zm64-3.38V171.5c12.91-3.13,23.84-7.79,32-13.57V172C232,180.39,219.59,189.4,200,194.87Z"></path></svg>
)
<svg
className={className}
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
fill="currentColor"
viewBox="0 0 256 256"
>
<path d="M184,89.57V84c0-25.08-37.83-44-88-44S8,58.92,8,84v40c0,20.89,26.25,37.49,64,42.46V172c0,25.08,37.83,44,88,44s88-18.92,88-44V132C248,111.3,222.58,94.68,184,89.57ZM232,132c0,13.22-30.79,28-72,28-3.73,0-7.43-.13-11.08-.37C170.49,151.77,184,139,184,124V105.74C213.87,110.19,232,122.27,232,132ZM72,150.25V126.46A183.74,183.74,0,0,0,96,128a183.74,183.74,0,0,0,24-1.54v23.79A163,163,0,0,1,96,152,163,163,0,0,1,72,150.25Zm96-40.32V124c0,8.39-12.41,17.4-32,22.87V123.5C148.91,120.37,159.84,115.71,168,109.93ZM96,56c41.21,0,72,14.78,72,28s-30.79,28-72,28S24,97.22,24,84,54.79,56,96,56ZM24,124V109.93c8.16,5.78,19.09,10.44,32,13.57v23.37C36.41,141.4,24,132.39,24,124Zm64,48v-4.17c2.63.1,5.29.17,8,.17,3.88,0,7.67-.13,11.39-.35A121.92,121.92,0,0,0,120,171.41v23.46C100.41,189.4,88,180.39,88,172Zm48,26.25V174.4a179.48,179.48,0,0,0,24,1.6,183.74,183.74,0,0,0,24-1.54v23.79a165.45,165.45,0,0,1-48,0Zm64-3.38V171.5c12.91-3.13,23.84-7.79,32-13.57V172C232,180.39,219.59,189.4,200,194.87Z"></path>
</svg>
);

export const BULK_DISCOUNT=0.1
export const MULTI_DISCOUNT=0.05
export const BULK_DISCOUNT = 0.1;
export const MULTI_DISCOUNT = 0.05;

const CheckoutTotal = ({ selectedProducts,paymentDisabled, companyId, user, postObject }) => {
const CheckoutTotal = ({
selectedProducts,
paymentDisabled,
companyId,
user,
postObject,
}) => {
const [totalPrice, setTotalPrice] = useState(0);

const [multiDiscountAmount, setMultiDiscountAmount] = useState(0);
Expand All @@ -20,6 +45,30 @@ const CheckoutTotal = ({ selectedProducts,paymentDisabled, companyId, user, post

const [totalPriceWithDiscount, setTotalPriceWithDiscount] = useState(0);

const [customDiscount, setCustomDiscount] = useState("");
const [verifiedDiscountAmount, setVerifiedDiscount] = useState(0);
const [loadingDiscount, setLoadingDiscount] = useState(false);

const checkDiscount = async () => {
setLoadingDiscount(true);
try {
const response = await axios.post("/api/lemonsqueezy/retrieveDiscount", {
customDiscount,
});

if (response?.data?.discount) {
setVerifiedDiscount(response?.data?.discount);
alert("Discount applied");
} else {
alert("Discount code invalid");
}
} catch (e) {
alert("Discount code invalid");
}
setLoadingDiscount(false);
};

console.log(verifiedDiscountAmount)
// create function to give total price from selected products - use setState
//total should be formik newsletter plus formik website
// get the prices from rawPrice of the selected products in SponsorPackages
Expand All @@ -35,8 +84,9 @@ const CheckoutTotal = ({ selectedProducts,paymentDisabled, companyId, user, post
let totalPurchases = 0;

//get all prices and add them together
selectedProducts?.forEach(p => {
// console.log(p);
for (let i = 0; i < selectedProducts?.length; i++) {
let p = selectedProducts[i];
// console.log(p);
//add the selected types
if (p?.type === "newsletter") {
selected_types.push("newsletter");
Expand All @@ -49,7 +99,7 @@ const CheckoutTotal = ({ selectedProducts,paymentDisabled, companyId, user, post
totalPurchases += p.dates?.length ? p.dates.length : 1;
//add the price
price += p.price * (p.dates?.length ? p.dates.length : 1);
});
}

if (
selected_types.includes("newsletter") &&
Expand All @@ -63,7 +113,7 @@ const CheckoutTotal = ({ selectedProducts,paymentDisabled, companyId, user, post
}

setTotalPrice(price.toFixed(2));

if (multipackDiscount) {
combodiscountamount = price * MULTI_DISCOUNT;
setMultiDiscountAmount(combodiscountamount.toFixed(2));
Expand All @@ -80,19 +130,29 @@ const CheckoutTotal = ({ selectedProducts,paymentDisabled, companyId, user, post
setBulkDiscountAmount(0);
}

//calculate total price with discount code
//check disocunt code
//if valid, apply discount
console.log('verifiedDiscountAmount', verifiedDiscountAmount)
if (verifiedDiscountAmount) {
// reduce the price by the discount percentage
price = price - price * (verifiedDiscountAmount / 100);
}

let totalPriceWithD = price - combodiscountamount - bulkdiscountamount;
//format with 2 decimal places
setTotalPriceWithDiscount(totalPriceWithD.toFixed(2));
};

useEffect(() => {
calculatePrice();
}, [selectedProducts]);
}, [selectedProducts, verifiedDiscountAmount]);

useEffect(() => {
calculatePrice();
}, []);


return (
<div>
<div className="rounded-lg border border-gray-200 overflow-hidden my-3">
Expand Down Expand Up @@ -154,7 +214,7 @@ const CheckoutTotal = ({ selectedProducts,paymentDisabled, companyId, user, post
<tr className="bg-blue-50 border-t ">
<td className="py-1 px-2 font-medium">Combo discount</td>
<td className="py-1 px-2 font-medium text-black/90">
💫 {MULTI_DISCOUNT*100}% off
💫 {MULTI_DISCOUNT * 100}% off
</td>
<td className="py-1 px-2 font-semibold">
–${multiDiscountAmount}
Expand All @@ -166,14 +226,30 @@ const CheckoutTotal = ({ selectedProducts,paymentDisabled, companyId, user, post
{bulkDiscountAmount ? (
<tr className="bg-blue-50 border-t ">
<td className="py-1 px-2 font-medium">Bulk discount</td>
<td className="py-1 px-2 font-medium text-black/90">💫 {BULK_DISCOUNT * 100}% off</td>
<td className="py-1 px-2 font-medium text-black/90">
💫 {BULK_DISCOUNT * 100}% off
</td>
<td className="py-1 px-2 font-semibold">
–${bulkDiscountAmount}
</td>
</tr>
) : (
""
)}
{verifiedDiscountAmount ? (
<tr className="bg-green-50 border-t">
<td className="py-1 px-2 font-medium">Discount code</td>
<td className="py-1 px-2 font-medium text-black/90">
💫 {verifiedDiscountAmount}% off
</td>
<td className="py-1 px-2 font-semibold">
–${(totalPrice * (verifiedDiscountAmount / 100)).toFixed(2)}
</td>
</tr>
) : (
""
)
}
{totalPriceWithDiscount ? (
<tr className="border-t bg-gray-50/90">
<td className="py-1 px-2 font-semibold">Grand Total</td>
Expand All @@ -192,27 +268,62 @@ const CheckoutTotal = ({ selectedProducts,paymentDisabled, companyId, user, post
<CoinsIcon className={"my- w-8 h-8"} />
<div className="flex flex-col ml-3 justify-center">
<h4 className="text-md font-semibold">
Get {multiDiscountAmount ? "an extra" : ""} {BULK_DISCOUNT*100}% off
Get {multiDiscountAmount ? "an extra" : ""} {BULK_DISCOUNT * 100}%
off
</h4>
<div className="my-auto text-sm">
Book 4+ weeks for {multiDiscountAmount ? "a further " : "a "}
<span className="font-">{BULK_DISCOUNT*100}% discount</span>.
<span className="font-">{BULK_DISCOUNT * 100}% discount</span>.
</div>
</div>
</div>
) : (
""
)}

<PurchaseButton
disabled={paymentDisabled}
selectedProducts={selectedProducts}
// newsletterDates={newsletterDates}
// websiteDates={websiteDates}
companyId={companyId}
user={user}
postObject={postObject}
/>
<div className="flex flex-col">
<label className="text-sm text-gray-700 mb-1">Discount code</label>
<div className="flex">
<input
className="h-[34px] mb-6"
type="text"
value={customDiscount}
onChange={e => {
setCustomDiscount(e.target.value);
}}
/>
<button
onClick={checkDiscount}
className={`${loadingDiscount ? "cursor-wait opacity-50" : ""} bg-gray-100 h-[34px] text-gray-700 text-sm font-medium px-4 py-1 rounded-lg ml-2`}
>
{loadingDiscount ? "Applying..." : "Apply"}
</button>
</div>
{verifiedDiscountAmount ? (
<div className="text-gray-700 -mt-4 text-sm my-4 flex bg-gray-50 p-2 rounded-lg px-3">
<HandHeartIcon className={"my- w-8 h-8"} />
<div className="flex flex-col ml-3 justify-center">
<h4 className="text-md font-semibold">Discount code applied</h4>
<div className="my-auto text-sm">
You saved {verifiedDiscountAmount}% on your total price.
</div>
</div>
</div>
) : (
""
)}

<PurchaseButton
disabled={paymentDisabled}
selectedProducts={selectedProducts}
customDiscount={customDiscount}
// newsletterDates={newsletterDates}
// websiteDates={websiteDates}
companyId={companyId}
user={user}
postObject={postObject}
/>
</div>
</div>
);
};
Expand Down
4 changes: 2 additions & 2 deletions components/Sponsor/DiscountBadge.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const DiscountBadge = ({ selectedTypes }) => {
<div className="flex flex-col ml-3 justify-center">
<h4 className="text-md font-semibold">Get 5% off</h4>
<div className="my-auto text-sm">
Combine with a website sponsorship for a{" "}
Combine 1 newsletter with 1 website sponsorship for a{" "}
<span className="font-">5% discount</span>.
</div>
</div>
Expand All @@ -23,7 +23,7 @@ const DiscountBadge = ({ selectedTypes }) => {
<div className="flex flex-col ml-3 justify-center">
<h4 className="text-md font-semibold">Get 5% off</h4>
<div className="my-auto text-sm">
Combine with a newsletter sponsorship for a{" "}
Combine 1 newsletter with 1 website sponsorship for a{" "}
<span className="font-">5% discount</span>.
</div>
</div>
Expand Down
3 changes: 2 additions & 1 deletion components/Sponsor/PurchaseButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import axios from "axios";
const PurchaseButton = ({
postObject,
user,

customDiscount,
selectedProducts,

companyId,
Expand Down Expand Up @@ -51,6 +51,7 @@ const PurchaseButton = ({
weeks: sponsorDates,
companyId: companyId,
postObject: postObject,
customDiscount: customDiscount,
});

console.log(response.data);
Expand Down
20 changes: 20 additions & 0 deletions pages/api/lemonsqueezy/purchaseProduct.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,26 @@ const productResponse = await axios.get(`${process.env.NEXT_PUBLIC_API_URL}/api/

});

if(reqData.customDiscount){
try{

const response = await lemonSqueezyApiInstance.get(`/discounts?filter[store_id]=${process.env.LEMON_SQUEEZY_STORE_ID}`);
//check if discount code exists
const discountItem = response.data.data.find(d => d.attributes.code === reqData.customDiscount);
const discountPercentage = discountItem.attributes.amount;

console.log(discountPercentage)
totalPrice = totalPrice - (totalPrice * discountPercentage / 100);
if(totalPrice==0){
totalPrice=1
}


}catch(e){
console.log('Error fetching discount')
}
}

// console.log(totalPrice);

return totalPrice
Expand Down
53 changes: 53 additions & 0 deletions pages/api/lemonsqueezy/retrieveDiscount.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// curl "https://api.lemonsqueezy.com/v1/discounts/1"
// -H 'Accept: application/vnd.api+json'
// -H 'Content-Type: application/vnd.api+json'
// -H 'Authorization: Bearer {api_key}'

// curl "https://api.lemonsqueezy.com/v1/discounts"
// -H 'Accept: application/vnd.api+json'
// -H 'Content-Type: application/vnd.api+json'
// -H 'Authorization: Bearer {api_key}'


import { lemonSqueezyApiInstance } from "@/lib/utils/lemonSqueezyAPI";

// https://docs.lemonsqueezy.com/api/discounts
export default async function handler(req, res) {

if (req.method !== "POST") {
// Only allow POST method for this endpoint
res.setHeader("Allow", ["POST"]);
return res.status(405).end(`Method ${req.method} Not Allowed`);
}

try {
// user check - returns error if no user found
//anyone can view product
// const {userId, user} = userCheck({req, res})

const reqData = req.body; // Directly use req.body for JSON payload

// console.log(user)
if (!reqData.customDiscount) {
return res.status(400).json({ message: "Discount code is required" });
}

// return res.status(500).json({ message: "An error occurred" });
const response = await lemonSqueezyApiInstance.get(`/discounts?filter[store_id]=${process.env.LEMON_SQUEEZY_STORE_ID}`);

// const response = await lemonSqueezyApiInstance.get("/products");

//check if discount code exists
const discount = response.data.data.find(d => d.attributes.code === reqData.customDiscount);

if(!discount){
return res.status(400).json({ message: "Discount code not found" });
}

return res.status(200).json({ discount:discount.attributes.amount });

} catch (error) {
console.error(error);
return res.status(500).json({ message: "An error occurred" });
}
}
2 changes: 1 addition & 1 deletion pages/sponsor/booking/[id]/payment.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ export default function SponsorPaymentPage({}) {
)}
</div>
</div>
<div className="col-span-6 md:col-span-2 sticky h-fit top-[160px]">
<div className="col-span-6 md:col-span-2 sticky h-fit top-[160px] mt-[80px]">
<div className="rounded-xl p-6 border border-opacity-20 bg-white">
{/* <h2 className="text-xl font-semibold">Total</h2> */}
<h1 className="text-xl font-semibold mx-auto mb-2">
Expand Down
4 changes: 2 additions & 2 deletions pages/sponsor/booking/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -335,9 +335,9 @@ const SponsorshipForm = ({
type="submit"
disabled={isSubmitting}
// disabled={errores}
className="w-full mt-3 h-10 px-3 rounded-lg bg-blue-600 hover:bg-blue-500 text-white"
className={`${isSubmitting?'cursor-wait opacity-50':''} w-full mt-3 h-10 px-3 rounded-lg bg-blue-600 hover:bg-blue-500 text-white`}
>
Save and Continue
{isSubmitting?'Saving...':'Save and Continue'}
</button>
</div>
</div>
Expand Down
Loading

0 comments on commit 6d60a7c

Please sign in to comment.