Sell from an app built with Lovable, v0 or Bolt

A prompt that builds a product page selling through a RevKeen checkout link, with the live price and hosted checkout, and no API keys.

AI app builders such as Lovable, v0 and Bolt can build a page that sells a RevKeen product. The simplest version needs no API key at all: the page links to a RevKeen checkout link, reads that link's current price from a public endpoint, and sends the buyer to RevKeen hosted checkout to pay.

What you need

  • A checkout link for the product, from Dashboard > Products > Checkout Links. Copy its URL; it looks like https://pay.revkeen.com/l/<link id>. Links on checkout.revkeen.com work too. See Checkout Links.
  • A test or sandbox account for your first purchase.

You do not need a publishable key, a secret key, an environment variable or a registered origin for this page. If a builder asks you for a RevKeen key, say no: nothing in this flow uses one, and a secret key must never be put in a web page.

The prompt

Paste this into the builder. Replace {{PAYMENT_LINK_URL}} with your link, and set {{CHANNEL}} to lovable, v0 or bolt.

Build a single product page that sells one item through a RevKeen payment link.

CONFIG (a plain constant in the source, not an environment variable; it is public):
  PAYMENT_LINK_URL = "{{PAYMENT_LINK_URL}}"
  CHANNEL = "{{CHANNEL}}"

1. Work out the link id and API origin from PAYMENT_LINK_URL.
   - The link id is the last path segment after "/l/". It is a UUID. If the URL
     has no "/l/<uuid>" segment, render a clear setup message instead of a buy
     button ("Add your RevKeen checkout link URL") and do nothing else.
   - API origin, from the URL host:
       pay.staging.revkeen.com or checkout.staging.revkeen.com
         -> https://staging-api.revkeen.com
       pay.revkeen.com or checkout.revkeen.com
         -> https://api.revkeen.com
     Any other host is not a RevKeen checkout link: render the same setup
     message and do nothing else.

2. On page load, read the live price in the browser:
     GET {apiOrigin}/public/payment-links/{linkId}
   Send it with fetch(url, { credentials: "omit", headers: { Accept: "application/json" } }).
   Send no API key, no Authorization header and no cookies. This endpoint is public.
   A successful response is:
     { "data": { "active": true, "product_name": string, "amount_minor": integer,
                 "currency": "GBP", "interval": "day"|"week"|"month"|"year"|null,
                 "interval_count": integer|null, "trial_period_days": integer|null,
                 "checkout_url": "https://..." } }
   Any link that cannot be bought returns HTTP 404 with
     { "code": "PAYMENT_LINK_UNAVAILABLE" }.

3. Show exactly one of these states:
   - LOADING: a placeholder where the price will be; the buy button is disabled
     and labelled "Loading…".
   - READY: data.active is true, amount_minor is a non-negative integer, currency
     is a 3-letter code and checkout_url starts with "https://". Show
     product_name and the price, and enable the buy button labelled "Buy now".
   - UNAVAILABLE: a 404, data.active not true, or any field failing the checks
     above. Show no price. The button is disabled and labelled "Unavailable".
   - ERROR: a network failure or any 5xx. Show no price. The button is disabled
     and labelled "Unavailable", with a small "Try again" control that repeats
     the read.

4. Format the price from minor units. Never divide by a hard-coded 100.
     const fmt = new Intl.NumberFormat(undefined, { style: "currency", currency });
     const digits = fmt.resolvedOptions().maximumFractionDigits;
     const text = fmt.format(amount_minor / 10 ** digits);
   Examples: 18000 GBP -> £180.00; 5000 JPY -> ¥5,000.
   If interval is set, add the cadence: "/ month", or "every 3 months" when
   interval_count > 1. If trial_period_days is set, add "N-day free trial".

5. The buy button is a real link (<a>) whose href is data.checkout_url with one
   query parameter added:
     const url = new URL(data.checkout_url);
     url.searchParams.set("rk_channel", CHANNEL);
   Open it in the same tab. Use the checkout_url from the response; never build
   a checkout URL yourself.

6. Rules. Do not break any of these:
   - Never type, store, compute or cache a price. The only price shown is the
     one from step 2, read on this page load. If the read fails, show no price.
   - Never ask for, create or use an API key (nothing starting with rk_live_,
     rk_sandbox_, rk_pk_ or sk_), and add no environment variables for RevKeen.
   - No backend, no server route, no database, no cart and no checkout form.
     Payment happens on RevKeen's hosted checkout page.
   - Do not collect card or bank details, and do not mention payment methods.
   - Do not add analytics, tracking or cookies for this.

7. Accessibility: the buy control is a link with a clear name; when disabled
   it has aria-disabled="true" and is not focusable as a live link; the price
   area uses aria-live="polite" so the loaded price is announced; focus styles
   stay visible; respect prefers-reduced-motion for any loading animation.

Keep the design simple: product name, price, one button, on a clean card.

How it works

  1. The page reads GET https://api.revkeen.com/public/payment-links/<link id> in the browser, with no credentials. The response contains the product name, the amount in minor units (for example 18000 for £180.00), the currency, any billing cadence or trial, and the hosted checkout_url.
  2. The page formats and shows that price. It never types, computes or stores a price of its own.
  3. Buy now links to the checkout_url from the response. Checkout works out the amount on RevKeen's servers, so nothing on the page can change what is charged.

If a link cannot be bought - archived, disabled, expired, tied to one customer, or mixing currencies or billing schedules - the endpoint answers 404 with PAYMENT_LINK_UNAVAILABLE, and the page shows no price and a disabled Unavailable button. The response may be cached for up to about a minute, so a price change can take that long to appear.

Check the generated code

Before you publish, look for these in the generated project:

  • No string starting rk_live_, rk_sandbox_, rk_pk_ or sk_, and no RevKeen environment variable.
  • No price typed into the code, and no / 100 on amount_minor. The price must come from the response.
  • The buy link uses checkout_url from the response, not a URL the code builds itself.
  • No server route, database or checkout form was added.

Good to know

  • Card payments. Checkout opened from a direct link takes card payments.
  • Confirming payment. A buyer returning to your app is not proof of payment. Use webhooks or the Dashboard.
  • Which app sold it. The page adds rk_channel=<builder> to the checkout URL. Recording that value against the order is being added and is not yet shown anywhere; checkout ignores the parameter until then.

Owner: david

On this page