Getting YouTube Channel ID via web extension

My problem is that I can get the ID of the channel that the extension user is on, but when the user switches to another channel, the YouTube HTML doesn’t update and leaves the old channel ID there. This breaks my extension because I always need the ID of the channel that the user is currently viewing.

How to get distinct values from an array of array in JavaScript? [duplicate]

I have the following array of array

const employees = [
    ["001","DDDDD"],
    ["002","EEEEE"],
    ["001","DDDDD"],
    ["002","EEEEE"],
    ["003","WWWWW"],
    ["001","DDDDD"],
];

I want to create another array which will contain only Unique values based on the first array element, e.g.

let uniqueArray = [
    ["001","DDDDD"],
    ["002","EEEEE"],
    ["003","WWWWW"],
]

What will be the best approach to achieve this? I can do a for loop on the original array, but there has to be a better way to do this.

Thanks

Problem with notification when adding product to cart

I am working on a project using JavaScript, without frameworks. I have a code for adding a product to the cart. The code works well. The product is added as I need it. But, with the notification about adding, something is wrong. I need, if the product is not yet in the cart, when adding, the notification Product in the cart (type success) appeared, and if the product is in the cart, it was not added and there was a notification This product is in the cart (type info). As I said, the product is added correctly, one product only once. But every time when clicking on the add to cart button, the notification Product in cart(type success) appears.

export class HomePage extends Component {
constructor() {
super();
this.template = template();
this.state = {
  links: [
   {
     label: "Sign In",
     href: ROUTES.singIn,
   },
   {
    label: "My Cart",
    href: ROUTES.singIn,
  },
  ],

  products: [],
  orderCart: [],
  user: null,
};
}

 setLinks = () => {
  const { getUser } = useUserStore();
  if (getUser()) {
    this.setState({
      links: [
        {
          label: "My Cart",
          href: ROUTES.cart,
        },
        {
          label: "LogOut",
          href: ROUTES.logOut,
        },
      ],
    });
  }
};

getProducts = () => {
apiService
.get("/products")
.then(({ data }) => {
  this.setState({
    ...this.state,
    products: mapResponseApiData(data),
  });
})
};

addToCard = async ({ target }) => {

const btn = target.closest('.add-to-cart');
if (!btn) return;

const { id, price, name, img } = btn.dataset;

if (this.state.orderCart?.some(item => item.id === id)) {
  useToastNotification({ 
    message: 'This product is already in the cart :)', 
    type: TOAST_TYPE.info });
  return;
}

await apiService.patch(`/order/${id}`, { id, price, name, img }).then(() => {
   this.setState({
   ...this.state,
   orderCart: this.state.orderCart?.concat({ id, price, name, img }),
    });
  })
  useToastNotification({ message: 'Product in the cart!', type: TOAST_TYPE.success });
};

 componentDidMount() {
this.setLinks();
this.getProducts();
this.addEventListener("click", this.addToCard);
 }

 componentWillUnmount() {
this.removeEventListener("click", this.addToCard);
 }
}

customElements.define('home-page', HomePage);

When I try to add a product without logging in, the notification works correctly. When I first click, the notification is type success, when I click again, type info. Maybe the problem is in the user state?

Store.js:

export class Store {
constructor(initialState) {
this._state = initialState;
 }

 getState() {
   return this._state;
 }

setState(state) {
this._state = Object.assign(this._state, state);
 eventEmitter.emit(EVENT_TYPES.store, { state: this._state });
} 

 subscribe(callback) {
 eventEmitter.on(EVENT_TYPES.store, callback);
}

 unSubscribe(callback) {
 eventEmitter.off(EVENT_TYPES.store, callback);
 }
  }

export const store = new Store({ user: null });

useUserStore.js:

export const useUserStore = () => {
const setUser = (user) => {
store.setState({ user });
};

const getUser = () => {
return store.getState().user;
};

const removeUser = () => {
store.setUser({ user: null });
};

 return {
 setUser,
 getUser,
 removeUser,
 };
};

Styling alertify dialog boxes

My code is here:
https://jsfiddle.net/0p71rzmv/4/

I have defined a new class of alertify dialog boxes with alertify.dialog. I would like to style these boxes, e.g., I would like the background color to be blue. I managed to modify the style of custom and error notifications of alertify but all my attempts to style my my_box boxes failed.

What are the CSS classes to modify for this ?
Is there anything to change in my JS ?

<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/alertify.min.js"></script>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<link rel="stylesheet" type="text/css"
      href="https://cdn.jsdelivr.net/npm/[email protected]/build/css/alertify.min.css"></link>
<link rel="stylesheet" type="text/css"
      href="https://cdn.jsdelivr.net/npm/[email protected]/build/css/themes/default.min.css"></link>
<style>
  .alertify-notifier .ajs-message {
      &.ajs-error {
          border: 5px solid;
          border-radius: 10px;
          background-color: #ff9999;
          border-color: red ;
      }
      &.ajs-custom {
          border: 5px solid;
          border-radius: 10px;
          background-color: #7777cc;
          border-color: blue;
      }
  }
</style>
<script>
  alertify.dialog('my_box', function factory() {
      return {
          main: function (message, onok) {
              this.setting('title', 'Confirm');
              this.setting('transition', 'fade');
              this.onok = onok;
              this.message = message;
          },
          setup: function () {
              return {
                  buttons: [
                      {
                          text: 'Yes',
                      },
                      {
                          text: 'No',
                      },
                  ],
                  options: {
                      modal: true,
                      resizeable: false,
                      maximizable: false,
                      closable: false,
                  },
                  focus: {
                      element: 1,
                  },
              };
          },
          prepare: function () {
              this.setContent(this.message);
          },
          callback: function (closeEvent) {
              if (closeEvent.index == 0) {
                  this.onok();
              }
          },
      };
  });
  $(document).ready(function () {
      alertify.notify("error", "error");
      alertify.notify("custom", "custom");
      alertify.my_box('Yes or no ?', function () {
          console.log('ok');
      });
  });
</script>

`Execution failed for task ‘:app:configureCMakeDebug[arm64-v8a] React Native + EXPO

Error part 1
Error part 2
Error part 3
Error part 4
Error part 5

The problem is downthere saying Task: react-native-worklets:configureCMakeDebug[arm64-v8a] FAILED.
I tried everything that i found on the internet to fix this problem, i have changed java destination folder installing it directly in C:, JAVA_HOME is C:Java, PATH: %JAVA_HOME%bin and also C:Program FilesCommon FilesOracleJavajavapath (i tried removing it, it reappears). Then i tried changing gradle versions i think one was <8.10, then second one 8.10.2 (i think) and now 8.14.3, no fix. I tried with every version setting newArchEnabled with false and true, no help. I added org.gradle.java.home=C:\Java in gradle.properties, no change. Changed Java version from v24 to v21 and then to jdk 17.

Redirect link from Gmail not opening my app, but works fine from Slack

I have an analytics tracking endpoint on my server that wraps links in emails.

The flow looks like this:

  1. In the email, the link is formatted as:

    https://myserver.com/track?redirect=https://myapp.com/some/page
    
  2. When the user clicks the link:

    • The request hits my server.
    • I store some analytics (trackId, metricId, user agent, IP, etc.).
    • Then I redirect() back to the original redirect URL.
  3. The app (myapp.com) has deep link support — when a browser sees this link, it should open the app directly if installed.

This works perfectly if I click the link from Slack.

But if I click the same link from Gmail, instead of opening the app, it stays in the browser.


Pseudocode of what I’m doing on the server

validate incoming query params (trackId, metricId, redirect)

extract user agent and ip

finalRedirect = decodeURIComponent(redirect)

if (metricId exists) {
   add metricId as query param to finalRedirect
}

store analytics (trackId, metricId, finalRedirect, userAgent, ip)

redirect (303) to finalRedirect

The Problem

  • From Slack → works fine, the app opens as expected.
  • From Gmail → it redirects to the browser but does not open the app, even though the app supports that host.

Changed to the redirect status code to 303 instead of 302, none of them worked.
Can’t reproduce the core issue as its happening on gmail.

If anybody has faced similar solution, please help

Overlapping HTML elements act as separate on Etsy — how?

Recently, I tried to replicate the image carousel from a product page on [etsy.com].(https://www.etsy.com/)

There are two HTML elements that visually overlap each other, but the browser handles them as if they were separate, non-overlapping elements.
By “handled by the browser,” I mean that you can’t simply use JavaScript to apply the :hover pseudo-class on element.

Here’s the DOM structure:
dom structure with problem

  • The scroll up / scroll down elements are responsible for auto-scrolling image carousel on hover.
  • Below them, there’s a scroll container div with the list of images.

On my site, since the scroll elements are positioned on top of the images, hovering only triggers them and not the images underneath.
But on Etsy, it works differently: you can hover the scroll zones and the images at the same time (you can test this by tweaking their styles).


That’s basically it — I can’t figure out if I’m just being a noob here, or if there’s actually something deeper behind this behavior. It’s been so frustrating to investigate further.

I made sure all the styles are matched perfectly and the DOM structure is as close as possible to Etsy’s. I even asked three different AIs and had them search the web. I tried asking about doing this in different frameworks, possible JavaScript workarounds, and CSS tricks (like pointer-events: none), but nothing worked.

Googling didn’t help either — I couldn’t find any solution to “set” :hover or somehow monkey-patch its behavior.

Right now, my workaround in React is to use a scrollUpRef and redirect all mouse events to the image element underneath. (I’m using document.elementsFromPoint(e.clientX, e.clientY) to find the image element.)
But as you can imagine, this isn’t native behavior at all. It doesn’t trigger the :hover pseudo-class on the image; instead, I just add CSS classes manually on mouseenter.

`core-js` with `@babel/preset-env` adds unnecessary polyfills and bloats bundle

I’m working on a web project built from an enterprise scaffold using webpack v5 and Babel. The target environment is Chrome ≥ 138 only.

However, I’ve noticed that the build includes unnecessary polyfills, even though the project is supposed to support only recent Chrome versions. After enabling the debug option in @babel/preset-env:

[
  '@babel/preset-env',
  {
    modules: false,
+    debug: true,
    useBuiltIns: 'usage',
    corejs: { version: "3.8", proposals: true }
  }
],

The debug output shows that Babel thinks Chrome 138 still requires polyfills for Set methods such as intersection, difference, and symmetricDifference:

Based on your code and targets, the corejs3 polyfill did not add any polyfill.

[/***/components/tooltip/style/index.ts]
Based on your code and targets, the corejs3 polyfill did not add any polyfill.

[/***/packages/utils/arrays.ts]
The corejs3 polyfill added the following polyfills:
  esnext.set.add-all { "chrome":"138" }
  esnext.set.delete-all { "chrome":"138" }
  esnext.set.difference { "chrome":"138" }
  esnext.set.every { "chrome":"138" }
  esnext.set.filter { "chrome":"138" }
  esnext.set.find { "chrome":"138" }
  esnext.set.intersection { "chrome":"138" }
  esnext.set.is-disjoint-from { "chrome":"138" }
  esnext.set.is-subset-of { "chrome":"138" }
  esnext.set.is-superset-of { "chrome":"138" }
  esnext.set.join { "chrome":"138" }
  esnext.set.map { "chrome":"138" }
  esnext.set.reduce { "chrome":"138" }
  esnext.set.some { "chrome":"138" }
  esnext.set.symmetric-difference { "chrome":"138" }
  esnext.set.union { "chrome":"138" }

[/***/packages/utils/animation.ts]
Based on your code and targets, the corejs3 polyfill did not add any polyfill.

But according to CanIUse, these features are already the baseline:

https://caniuse.com/mdn-javascript_builtins_set_symmetricdifference

CanIUse screenshot for compatibility of symmetricDifference

Note: babel-loader, @babel/preset-env, @babel/polyfill, core-js, caniuse-lite, browserslist are all updated to the latest version.

How can I prevent @babel/preset-env + core-js from including these unnecessary polyfills for modern Chrome? Is there a recommended way to handle this issue, other than disabling useBuiltIns entirely?

Is it recommended in TypeScript to define event handler types separately and use them with const functions? [closed]

When defining functions in TypeScript, is the following approach — defining event types in a separate types.ts file and then declaring functions as const with that type — considered a recommended practice in modern development?

Example Code (feels a bit cumbersome to me)

types.ts:

export type MouseEventHandler = EventHandler<MouseEvent>;

event-listeners.ts:

export const onClickCreateItem: MouseEventHandler = async (event, deps) => {
  // some logic
};

Current Code (what I usually do)

event-listeners.ts:

export async function onClickCreateItem(
  event: MouseEvent,
  deps: CreateItemDependencies
) {
  // some logic
}

Notes

  • I’m fairly new to TypeScript, and my impression is: “Wait, do I really need to set up this much boilerplate just for types?”
  • If the answer is basically “Yes, that’s just how TypeScript is, and it’s considered good practice,” then I’ll happily follow that approach.
  • I asked ChatGPT, but its answers vary depending on how I phrase the question — sometimes it says one approach is recommended, sometimes the other. So I’d love to hear opinions from real developers here.

Thanks in advance!

How to join (+2347063372861%% I want to join occult for money ritual

MERCURIOUS CONFRATERNITY

Mercurious occult Member +2347063372861

WELCOME TO THE WORLD OF BILLIONAIRE MERCURIOUS CONFRATERNITY OCCULT SOCIETY WHERE TO ACHIEVE ALL YOUR DESIRE IN LIFE, JOIN US NOW AND BE FREE FROM POVERTY AND PAINS, WE ARE HERE TO CHANGE YOU FROM BAD TO GOOD ONCE YOU HAVE THE MIND TO DO WHAT IT TAKE TO MAKE WEALTH AND FORTUNES CALL +2347063372861 NOW! Do you want to be a member of MERCURIOUS OCCULT as a brotherhood that will make you rich and famous in the world and have power to control people in the high place in the worldwide .Are you a pastor, business man or woman, artist, politician, musician, student, do you want to be rich, famous, powerful in life, join the Mercurious fraternity cult today and get instant rich sum of 250 million naira in a month, and a free home.

BENEFITS GIVEN TO NEW MEMBERS WHO JOIN MERCURIOUS

  1. A Cash Reward of USD $800,000 USD

  2. A New Sleek Dream CAR valued at USD $500,000 USD

3.A Dream House bought in the country of your own choice

  1. One Month holiday (fully paid) to your dream tourist destination.

  2. One year Golf Membership package

  3. A V.I.P treatment in all Airports in the World

  4. A total Lifestyle change

  5. Access to Bohemian Grove

  6. Monthly payment of $1,000,000 USD into your bank account every month as a member

  7. One Month booked Appointment with Top 5 world Leaders and Top 5 Celebrities in the World.

If you are interested of joining us in the great brotherhood of BILLIONAIRES MERCURIOUS Occult temple CALL NOW FOR ENQUIRIES +2347063372861

HAVE YOU YOU BEEN SEEKING FOR OPPORTUNITIES TO JOIN A SACRED BROTHERHOOD OCCULT FOR MONEY RITUAL IN NIGERIA? This is your time to make a positive change in your life, in as much as you have the bravery and courage to withstand the difficult parts of this brotherhood. +2347063372861, Every member of this Society is entitled to all the Secret knowledge of Spiritual wealth and power, money is assured, power is assured, fame is assured if you want it, protection is also assured. But have it in mind that becoming a member of this occult you’re to perform a great sacrifice to please the Lord Spiritual and all your heart desires will be granted. Call now for enquiries +234706337286 BILLIONAIRES MERCURIOUS OCCULT IS A SACRED FRATERNITY WITH A GRAND LODGE TEMPLE SITUATED IN ENUGU STATE NIGERIA, OUR NUMBER ONE OBLIGATION IS TO MAKE EVERY INITIATE MEMBER HERE RICH AND FAMOUS IN OTHER RISE THE POWERS OF GUARDIANS OF AGE+. +2347063372861..

Is it recommended in TypeScript to define event handler types separately and use them with const functions?

When defining functions in TypeScript, is the following approach — defining event types in a separate types.ts file and then declaring functions as const with that type — considered a recommended practice in modern development?

Example Code (feels a bit cumbersome to me)

types.ts:

export type MouseEventHandler = EventHandler<MouseEvent>;

event-listeners.ts:

export const onClickCreateItem: MouseEventHandler = async (event, deps) => {
  // some logic
};

Current Code (what I usually do)

event-listeners.ts:

export async function onClickCreateItem(
  event: MouseEvent,
  deps: CreateItemDependencies
) {
  // some logic
}

Notes

  • I’m fairly new to TypeScript, and my impression is: “Wait, do I really need to set up this much boilerplate just for types?”
  • If the answer is basically “Yes, that’s just how TypeScript is, and it’s considered good practice,” then I’ll happily follow that approach.
  • I asked ChatGPT, but its answers vary depending on how I phrase the question — sometimes it says one approach is recommended, sometimes the other. So I’d love to hear opinions from real developers here.

Thanks in advance!

Detect browser support for “closedby” attribute

The closedby attribute is supported in all major browsers except Safari. For those browsers, setting closedby="all" on a dialog element enables light dismiss, including clicking on dialog::backdrop.

I tried to implement feature detection for Safari as follows (this is inside a click event listener):

// modal.close() is handled by `closedby` attribute on <dialog>
// except in Safari.
if (`closedby` in navigator) {
  console.log(`closedby supported`);
} else {
  modal?.close();
  console.log(`closedby not supported`);
};

But even on supporting browsers, the console message is closedby not supported.

How can I detect support for closedby?

Note: modal is a variable holding the dialog element.

How to integrate Paypal with mern stack, the correct way?

State machine (definitive)

cartreserved (or pending) → payment_in_progresspaidfulfilled

Failures / alternatives: reservedexpiredcancelled (stock returned); payment_in_progressfailedcancelled (release stock or keep for manual review); paidrefunded (store refund events).

Make allowed transitions explicit in code to prevent invalid state changes.


Global references you must read first (most important)

  • PayPal Orders API (create / capture orders) — read fully.
  • PayPal JS SDK reference (createOrder / onApprove) and checkout flow examples.
  • PayPal Webhooks & verify signature guide.
  • MongoDB Aggregation Pipeline (for analytics).
  • MongoDB Transactions (production constraints: replica sets).
  • MongoDB Text Index / Search docs (product search).
  • Redux Toolkit docs (slices, configureStore, thunks).
  • Express.js docs (routing / middleware best practices).

Use the above pages while implementing each corresponding module


Module-by-module implementation (algorithms + exact references to read)

A — Backend core (Express + DB)

Read before coding: Express guide (routing & middleware).

1) Project skeleton & middleware

Algorithm (no code):

  1. Create Express app and structured router directories: /routes, /controllers, /models, /services, /utils.

  2. Add JSON body parser, CORS (restrict origins in prod), Helmet for headers, rate limiter for sensitive endpoints, error handler middleware.

  3. Load .env and validate presence of required keys (MONGO_URI, PAYPAL_CLIENT_ID, PAYPAL_SECRET, WEBHOOK_ID).

    Where to read:

  • Express docs for middleware and error handling.

2) DB connection & transactions

Algorithm:

  1. Connect to MongoDB — ensure it is a replica set to enable transactions (Atlas or single-node replica). If not possible, fall back to atomic findOneAndUpdate + compensating rollback.
  2. Expose a helper to start sessions and transactions for multi-document work (order creation + stock updates). If session/transaction creation fails (standalone), use per-item atomic updates with a compensating rollback algorithm and tight logging.

Read:

  • MongoDB production transactions doc.

3) Models & indexes (MongoDB)

Design checklist (fields):

  • products: _id, name, description, category, price, currency, stock, imageUrl, createdAt, updatedAt
    • Create text index on name + description for search.
    • Additional indexes: category, price, stock (for low stock queries).
  • orders: _id, orderNumber, items[{productId, qty, priceAtPurchase}], totalAmount, currency, customer{}, status, paypal:{orderId,captureId,raw}, createdAt, expiresAt, paymentEvents[]
  • counters or use a sequence generator for orderNumber.

Read:

  • MongoDB indexes, text index docs.

4) Product endpoints

Algorithm:

  • GET /products:
    1. Parse q, category filters, price range, sort, page, limit.
    2. Use $text search if q present; project score. Apply filters; send pagination meta. (If you need better search later, use Atlas Search.)
  • GET /products/:id: validate id, return product details.

Read:

  • MongoDB text search / aggregation basics.

5) POST /orders — create + reserve stock (core corrected algorithm)

This is critical — follow exactly.

Algorithm:

  1. Validate request (items array and user details). Do not trust client totals.
  2. Recompute totals server-side by fetching current price for each product and summing price * qty. Store priceAtPurchase per item. (This prevents price manipulation.)
  3. Start a transaction session (if replica set available). Inside transaction:
    • For each item run an atomic conditional update: findOneAndUpdate({_id: productId, stock >= qty}, {$inc: {stock: -qty}}). If any update fails (not enough stock), abort transaction and return which items are out of stock.
    • Create order document with status = "reserved", expiresAt = now + RESERVATION_TTL (e.g., 15 minutes). Store orderNumber (get next from counters via atomic $inc).
  4. Commit transaction; return orderId + expiresAt.
  5. If transactions unavailable:
    • For each item findOneAndUpdate with stock >= qty. Track successful updates. If any fail, do compensating increments for previously decremented products (careful to log failures and retry). Create order only after all decrements succeed. (This is less safe than transactions but workable.)

      Why this corrected approach:

  • Reserving stock at order creation prevents race where payment capture later would succeed for an out-of-stock item. This requires atomicity across multiple product updates — hence transactions are recommended.

6) POST /paypal/create-order (server create PayPal order)

Algorithm:

  1. Client sends internal orderId. Server re-fetches order and recomputes/validates amounts.
  2. Call PayPal Orders API server-side to create a PayPal order with the authoritative amount and line items. Save returned paypal.orderId in orders.paypal.orderId and update order status = "payment_in_progress". Return paypal.orderId to client. (Do not let client create PayPal order with client-side totals.)

7) Capture flow (recommended server capture + webhook fallback)

Algorithm (server capture endpoint):

  1. Client gets approval via PayPal JS UI and calls server POST /paypal/capture with orderId (internal) & paypalOrderId.
  2. Server calls PayPal Orders capture endpoint server-side, verifies:
    • captured amount equals expected order.totalAmount and currency matches,
    • payer status is valid.
  3. If success:
    • Update orders.status = "paid", store captureId and raw response in orders.paypal.
    • Create fulfillment actions (email invoice, update analytics).
  4. If capture fails due to network/timeouts: mark payment_in_progress and queue retry; DO NOT mark paid unless verified.

Webhook redundancy:

  • Implement POST /webhooks/paypal to accept PayPal events (e.g., PAYMENT.CAPTURE.COMPLETED) and verify each webhook using PayPal signature verification or verify endpoint. Use idempotent processing (store PayPal event id).

Read:

  • PayPal Orders API + capture docs and JS SDK.

8) Webhooks (precise)

Algorithm:

  1. Receive event; verify with PayPal using verify-webhook-signature or equivalent; reject if verification fails.
  2. Check event idempotency store (if processed, return 200).
  3. Find related order by paypal.orderId or by captureId in payload. Re-verify amounts if necessary via server call to PayPal.
  4. Update orders.status accordingly (e.g., paid), store raw event, and ack.

B — Stock expiry & reconciliation worker (must do)

Algorithm (no code):

  1. Run background job every N minutes (e.g., every 1–5 minutes). Query orders where status = "reserved" and expiresAt < now.
  2. For each expired order:
    • Start transaction (if possible) and increment products.stock by each reserved qty (reverse the decrements) and update orders.status = "expired" / cancelled. Store a release event in order.paymentEvents.
    • If transaction not available, perform safe per-item increment with retry and update order status.
  3. Notify user via email or show message on retry.

Why: TTL deletes will not restore stock; TTL is for deletion only — so we implement this worker to release reserved stock reliably.


C — Frontend (React + Redux Toolkit)

Read before coding: Redux Toolkit usage & React fundamentals.

1) State design (Redux slices)

Logical slices:

  • productsSlice — search results, product details, loading/errors.
  • cartSlice — cart items, computed totals, persist to localStorage (rehydrate on load).
  • orderSlice — create order (reservation), track orderId, expiresAt, order status.
  • uiSlice — global toasts / loaders.

Algorithm for checkout UI flow:

  1. User clicks checkout → open Checkout form page. Validate with react-hook-form or plain validation.
  2. On submit, call POST /orders (server will compute totals and reserve stock). If server returns stock error, update cart & show item specific messages.
  3. If order reserved, call POST /paypal/create-order to get paypal.orderId (server side), then place PayPal button on page using PayPal JS SDK with createOrder returning server order id (or pass paypalOrderId to SDK). On approval, call server POST /paypal/capture.
  4. Show order/:orderNumber status page with polling or server push to show paid once webhook processed.

Read:

  • PayPal JS SDK createOrder/onApprove flow.

2) UX pitfalls & fixes

  • Show reservation expiry countdown to user (from expiresAt) so user knows time left.
  • Handle server rejection on order creation (stock changed) gracefully — update UI and let user continue.
  • Persist cart in localStorage and allow merging with server cart if user logs in (optional).

D — Analytics & aggregation (MongoDB)

Algorithm (analytics endpoint):

  1. For sales by category:
    • Match orders.status = "paid", date range filter.
    • $unwind items, $group by items.category (or product ref) summing qty and revenue = qty * priceAtPurchase.
    • $sort by revenue/qty, $limit. Use $project to shape results. (This is standard aggregation pipeline use.)

Read:

  • Aggregation pipeline overview & stage reference.

E — Security, validation & infra

Security checklist (read & implement)

  • Validate + sanitize all request bodies (Joi / AJV or manual).
  • Use Helmet, set secure CORS, use rate limiting on endpoints (esp POST /orders, /paypal/*, login).
  • Use HTTPS in production; store secrets in env.
  • Verify PayPal webhooks signatures; store processed event IDs to avoid duplicates.

Infra

  • Use MongoDB Atlas (supports transactions) or configure single-node replica set for local testing.
  • Host Node backend on Heroku/Render/VPS — ensure stable URL for PayPal webhooks. Add health checks and logging.


Testing checklist (how to validate correctness)

  1. Unit tests: price calculation, order total recomputation, stock decrement/increment logic.
  2. Integration tests: multi-item order create with concurrent requests for last unit — ensure only one reserves. (Simulate concurrency.)
  3. End-to-end: Use PayPal sandbox to create and capture; also simulate webhook calls from PayPal sandbox or via Postman. Verify server receives and verifies webhook and marks order paid.
  4. Failure tests: network failures during capture (server retry logic), webhook duplicate handling, reservation expiry worker behavior.
  5. Analytics tests: seed sample paid orders and validate aggregation results.

Quick “what to read for each module” (one-line links to docs)

  • Express.js basics & error handling.
  • MongoDB transactions & replica set constraints.
  • MongoDB aggregation pipeline (analytics).
  • MongoDB text index & search best practices (product search).
  • PayPal Orders API (create/capture) — server integration.
  • PayPal JavaScript SDK (createOrder/onApprove) — client integration.
  • PayPal Webhooks & verification best practices.
  • Redux Toolkit usage & configureStore/tutorial.

Final, exact recommended sequence (refined & safe)

  1. Repo + env skeleton + docs references (above).
  2. Backend skeleton (Express, middleware, DB connect to a replica set or Atlas).
  3. Implement products endpoints + seed DB + text index.
  4. Implement counters & order model.
  5. Implement POST /orders with transactional reserve algorithm (or safe fallback).
  6. Implement POST /paypal/create-order (server), integrate PayPal Orders API.
  7. Implement POST /paypal/capture server capture + POST /webhooks/paypal with signature verification + idempotency.
  8. Implement reservation expiry worker and reconciliation.
  9. Frontend basic build (React + Redux Toolkit) and connect to backend flows.
  10. End-to-end tests with PayPal sandbox; fix race conditions.
  11. Demo video following earlier checklist.


Phase 1: Backend Setup

  1. Initialize Project

    • npm init -y, install: express, mongoose, cors, dotenv, express-validator, paypal-rest-sdk (or official PayPal SDK).

    • Setup folder structure:

      /backend
        /models
        /routes
        /controllers
        /config
      
      
  2. Configure MongoDB

    • Use mongoose.connect(process.env.MONGO_URI).
    • Ensure indexes for product name/description.
  3. Create Models

    • Product Model: name, price, category, stock, imageURL, description.
    • Order Model: customer info, items (with product references), status, PayPal transaction ID.
  4. Core APIs

    • /api/products → list, filter, search (with regex or $text index).
    • /api/products/:id → single product.
    • /api/orders → create order (validations).
    • /api/orders/:id → get order details.
    • /api/paypal/create-order → initiate PayPal order.
    • /api/paypal/capture-order → confirm payment + update order status.
    • /api/analytics → MongoDB aggregation (sales by category, top sellers, low stock).
  5. Middleware

    • Error handler for 400/500 responses.
    • Validation middleware (express-validator).

Phase 2: Frontend Setup

  1. React App Setup
    • npx create-react-app frontend
    • Install: react-router-dom, @reduxjs/toolkit, react-redux, axios.
  2. Redux Setup
    • Store structure:

      /frontend/src
        /store
          store.js
          productSlice.js
          cartSlice.js
          orderSlice.js
      
      
    • Slices:

      • productSlice → fetch, search, filter.
      • cartSlice → add/remove/update items, persist in localStorage.
      • orderSlice → create order, track status.
  3. Components
    • Header → nav + cart icon.
    • ProductList → grid of product cards.
    • ProductCard → image, name, price, add-to-cart.
    • Cart → items, quantity control, summary.
    • Checkout → form for customer info, order summary.
    • PayPalButton → integrate SDK.
    • OrderConfirmation → display after payment.

Phase 3: Data Flow & Integration

Correct Flow:

  1. User → sees product list (from /api/products).
  2. User → adds items to cart (Redux → localStorage).
  3. User → goes to checkout:
    • Enter info → create order (/api/orders).
  4. App → call /api/paypal/create-order → PayPal popup opens.
  5. On success → PayPal returns transaction ID → call /api/paypal/capture-order.
  6. Backend → updates order status → return success response.
  7. Frontend → show confirmation page.

Phase 4: Testing

  • API testing: use Postman (products, orders, PayPal flow, analytics).
  • Frontend testing: ensure cart persists after refresh, errors handled gracefully.
  • Payment testing: use PayPal Sandbox test card.

Phase 5: Demo Preparation

  1. Walkthrough Flow:
    • Product search/filter.
    • Cart → Checkout → Payment → Confirmation.
    • Show analytics API in Postman.
  2. Redux DevTools: show cart & order slices updating.
  3. Explain Code Structure: highlight models, controllers, slices.

References (for each module)


Corrections to Flow

  • Always create order in DB before PayPal payment → ensures rollback if payment fails.
  • Cart must persist in localStorage → prevents losing items on refresh.
  • Analytics should be separate endpoint (not mixed in main APIs).
  • Never directly trust PayPal frontend response → always verify in backend with PayPal SDK.