DEV Community

holdi
holdi

Posted on

How to Deliver Digital Downloads Automatically with No Backend

You do not need a server to sell digital files. You do not need a database to track downloads. You do not need a monthly bill for Stripe or PayPal processing.

As a developer or freelancer, the most frustrating part of selling digital assets isn't writing the code. It is the operations overhead: uploading files to a cloud bucket, managing expiration links, and dealing with customers who say "the download failed."

You can build a storefront that handles delivery end-to-end on the frontend. This is practical, secure enough for most use cases, and removes your reliance on backend infrastructure.

Here is the practical guide to doing it.

1. The Client-Side Payment Strategy

You need a payment processor that runs entirely in the browser. Stripe Checkout is the standard here. It is a hosted page, which is good for PCI compliance, but it needs to handle the return logic.

When the user completes payment, Stripe sends a postback (webhook) or you can handle the response via your frontend code if you are using Elements.

For this architecture, we use Stripe Elements with the Payment Intents API. The frontend initiates the payment. Once Stripe confirms success, we swap the UI from a "Pay" button to a "Download" state without ever talking to your server.

2. The File Hosting Setup

You need a place to store the files. Do not use your main website’s public folder or a local machine. You need a dedicated CDN or object storage.

For simplicity, use Cloudflare R2 (S3 compatible) or Netlify Drop. If you are using Next.js, you can place your assets in the public folder, but for a scalable "store" feel, use a static storage provider. Upload your file (e.g., asset.zip) and copy the direct URL.

Example folder structure:

/my-store
  /assets
    /premium-guide.pdf
    /source-code.zip
Enter fullscreen mode Exit fullscreen mode

3. The Architecture: Client-Side Flow

The logic is simple:

  1. User fills credit card.
  2. You verify payment on the client (or backend proxy).
  3. On success: You show a download link.

Here is a practical React example using Stripe Elements.

Step A: Initialize Payment
Create a function that calls Stripe to create a PaymentIntent. This ensures the card is valid.

// Create a PaymentIntent on your backend to check if they paid.
// If this were zero-backend, you might simply trust the client for demo purposes,
// but for a real store, always validate with your backend.
const { clientSecret } = await fetch('/api/create-payment-intent', {
  method: 'POST',
  body: JSON.stringify({ amount: 2000, currency: 'usd', itemId: 'premium-guide' }),
}).then(r => r.json());

// Confirm the payment
const { error, paymentIntent } = await stripe.confirmCardPayment(clientSecret, {
  payment_method: { card: cardElement },
});
Enter fullscreen mode Exit fullscreen mode

Step B: Handle Success & Deliver
If error is null, the payment went through. Now, you must deliver the file. Since we have no backend, we retrieve the file URL from our storage directly.

if (!error) {
  // The payment succeeded.
  const fileUrl = "https://your-cdn.com/assets/premium-guide.pdf";

  // You can trigger the download immediately
  window.location.href = fileUrl;

  // Or show a nice UI
  setDownloadState({ 
    status: 'paid', 
    downloadUrl: fileUrl,
    fileName: 'Premium_Guide.pdf'
  });
}
Enter fullscreen mode Exit fullscreen mode

4. Security: How to Prevent Free Rides

The biggest risk of "no backend" is that a user pays, gets the link, and then shares that link with a friend. Without a server to check "Has this user ID paid?", you have no defense against link sharing.

To solve this, you use Signed URLs.

You cannot generate a valid signed URL on the frontend (this would require exposing your secret key). Instead, you send a request to a tiny "middleware" function on your backend that does two things:

  1. Checks if the user has a valid session or payment record.
  2. Generates a temporary signed URL for the file (valid for 15 minutes).
  3. Returns that signed URL to the frontend.

The frontend then hides the raw CDN link and shows the user only the signed link provided by your tiny middleware.

Example Middleware (Node/Express)

app.get('/api/get-file/:id', (req, res) => {
  // 1. Validate user session or check DB for payment status
  const userPaid = checkUserPaymentStatus(req.session.userId);

  if (!userPaid) {
    return res.status(403).send('Access Denied');
  }

  // 2. Get the signed URL from your storage provider
  // R2, CloudFront, or AWS S3 presigned URLs are standard here
  const signedUrl = s3Client.getSignedUrl('GetObject', {
    Bucket: 'my-assets',
    Key: `assets/${req.params.id}`,
    Expires: 900 // 15 minutes
  });

  res.json({ url: signedUrl });
});
Enter fullscreen mode Exit fullscreen mode

This approach gives you the security of a backend without running a complex application server for file serving.

5. Managing Digital Goods at Scale

If you are selling 1,000s of items, you need an automated way to handle file uploads. You can write a script that hits the storage API to upload files, and then simply save the metadata (filename, price, file URL) in a simple JSON file or a cheap database like Supabase.

When a customer buys, your middleware just reads that JSON file to verify ownership.

Summary of the Stack

  • Payments: Stripe Elements + Payment Intents
  • Hosting: Cloudflare R2 / Netlify Drop (Static)
  • Middleware: A single Express endpoint for Signed URLs
  • Delivery: window.location.href on success

This removes the friction of deployment and allows you to focus on the product. You don't need to worry about hosting costs or downtime affecting your customers.


I sell these kinds of digital packs in a tiny automated Telegram store - instant USDT delivery. Check it: https://t.me/m3lmhermes_bot

Top comments (0)