Sell from your own website
Market your app on your own site and let UseThatApp handle the money. Your pricing page fetches live prices from the public Pricing API, your Buy buttons link to a hosted checkout on usethatapp.com, and the buyer lands back in your app with their purchase ready to verify — no webhook handling, no payment code, no tax logic on your side.
Pick how your app verifies the purchase
Steps 1–3 below are the same either way. Step 4 is where the two modes differ: license keys if your app keeps its own accounts (included in the per-sale fee), or the Hosted sign-in add-on if you would rather we ran login too. Selling never requires our login.
UseThatApp is the merchant of record
UseThatApp processes the payment, calculates and remits applicable tax, handles refunds and chargebacks, and appears on the buyer's card statement. You get payouts via Stripe. Your website never touches card data or tax rules — "Buy" is just a link.
Who can sell, and who can buy
Sellers must be based in the United States: payouts run through Stripe Connect, and onboarding asks for a US identity and bank account. Buyers can be in the United States, the European Union, Canada, Australia and the other countries offered at checkout (enforced on both the declared tax address and the card's billing address). The two lists are independent — buyer reach does not extend seller eligibility.
A buy link resolves only while your seller account is verified, the app is published, the product is active, and external sales is switched on for the app (on by default). Otherwise it returns 404; the manage page's readiness list shows which gate is closed.
The whole journey
- A visitor lands on your pricing page. Your page fetches live prices from the Pricing API (public, CORS-enabled, no auth) and renders a Buy button per plan using each price's ready-made
buy_url. - They click Buy. The link is a hosted purchase link on usethatapp.com: one page for email, country and postal code (EU businesses can add a VAT number), card entry via Stripe, then a confirmation. New buyers get an account auto-created (no password, no verification wall); existing accounts sign in normally, including MFA.
- They return to your app. The license is created synchronously during checkout, so the purchase is real the moment they land — no webhook lag, no polling. Where they land is your
nextURL, or your app's registered Login URL by default. - You verify, in whichever mode you chose. With license keys, the return carries a
uta_orderreference you exchange for the buyer's key, then validate it from then on. With Hosted sign-in, the OIDC flow signs them in andget_entitlementanswers from that session. Both unlock features by the stableproduct_public_id— the sameprod_…id the Pricing API returns asproduct_idon each price. - Later, they manage the subscription (upgrade, auto-renew, billing portal) via a manage link back on usethatapp.com.
Step 1 — Render live prices on your site
Fetch prices straight from the browser — the endpoint is anonymous and CORS-enabled. Never hardcode prices: the moment you edit a price on the dashboard, a hardcoded pricing page is wrong. Use the returned buy_url for your Buy buttons instead of assembling URLs yourself. Your client_id is on the dashboard's Integration page.
<div id="pricing">Loading plans…</div>
<script>
const CLIENT_ID = "<your-client-id>";
const label = (p) =>
p.is_recurring ? `${p.currency.toUpperCase()} ${p.amount} / ${p.frequency}`
: `${p.currency.toUpperCase()} ${p.amount} one-time`;
fetch(`https://www.usethatapp.com/api/v1/public/apps/${CLIENT_ID}/prices/`)
.then((res) => res.json())
.then(({ prices }) => {
document.getElementById("pricing").innerHTML = prices
.map(
(p) => `
<div class="plan">
<h3>${p.product_name}</h3>
<p>${label(p)}</p>
<a class="buy" href="${p.buy_url}">Buy ${p.product_name}</a>
</div>`
)
.join("");
});
</script>Responses are cacheable for 60 seconds and rate-limited at 120 requests/minute per IP — fine for a pricing page, no proxy needed. If your site has a server, the SDK does the same with types — and needs only UTA_CLIENT_ID, not the OAuth secrets:
Python
from usethatapp import get_prices
plans = get_prices()
for p in plans.prices:
render_plan(p.product_name, p.amount, p.currency,
p.frequency, p.buy_url)JavaScript
import { getPrices } from "usethatapp";
const { prices } = await getPrices();
for (const p of prices) {
renderPlan(p.product_name, p.amount, p.currency,
p.frequency, p.buy_url);
}The full response shape (including has_free_tier and the product_id that matches the entitlement’s product_public_id) is on the Pricing API page.
Step 2 — Point Buy at the hosted checkout
Each price's buy_url is already a complete checkout link. To add parameters, use the SDK's pure URL builder — purchase_url(price_id, next=…) / purchaseUrl(priceId, { next }); it makes no network call and needs no configuration. Two optional params are worth knowing (full reference: Purchase links):
next— where the buyer returns after purchase. Must be HTTPS on your app's registered domain (localhostallowed for development). Leave it off and the buyer is sent to your registered Login URL — what you want with Hosted sign-in, since OIDC signs them in with the fresh entitlement. In keys mode, pointnextat the route that readsuta_order, which is appended to whichever URL the buyer returns to.ref— an affiliate code. Plain buy links are all you need: a sale with norefbills at the default seller rate (current rates at usethatapp.com/pricing). When a third-party affiliate sends you a buyer, forward the incomingrefonto your buy link (or let them append?ref=CODE): it sets the 60-day attribution cookie once the visitor accepts optional cookies (held for the session until then), that sale bills at the platform rate instead, and the affiliate's commission is paid out of the difference — never on top. You never need your own code on your own links; arefnaming an app's own seller is ignored.
from usethatapp import purchase_url
purchase_url("prc_9f2kq8w7", next="https://yourapp.com/welcome", ref="partner42")
# → https://www.usethatapp.com/buy/prc_9f2kq8w7/?next=https://yourapp.com/welcome&ref=partner42Step 3 — What the buyer experiences
- One page asking for email, country, and postal code (plus an optional VAT number for EU businesses). Existing UseThatApp accounts are routed through normal sign-in (including MFA); new buyers get an account auto-created with no password and no verification wall.
- Card entry via Stripe, then a confirmation page.
- A success page with a "resend receipt email" button, then the redirect back into your app.
The receipt email contains the receipt, an email-verification link, a set-a-password link (for auto-created accounts), and a manage link. Buyers who already own the product are never double-charged — a buy link recognizes them and sends them straight back to your app.
Step 4 — Verify and gate when they return
This is the step that depends on your mode. Both paths end at the same check, and both gate on the stable product_public_id.
If you keep your own auth (license keys)
The return URL carries a uta_order reference. Exchange it once for the buyer's license key, store the key against your own user, and validate it from then on — your login never changes. Full walkthrough: License Keys.
Python
from usethatapp import get_order
order = get_order(request.GET["uta_order"])
user.license_key = order.license_key
user.save()
# later, on any request:
# state = validate_license_key(user.license_key)JavaScript
import { getOrder } from "usethatapp";
const order = await getOrder(req.query.uta_order);
await saveLicenseKey(user, order.license_key);
// later, on any request:
// const s = await validateLicenseKey(user.licenseKey);If we run login (Hosted sign-in add-on)
The buyer arrives at your Login URL, your existing Sign in with UseThatApp integration signs them in, and your server reads the live plan. Gate on the stable prod_… id — product_id and product_public_id are equal-valued aliases, and both match the product_id on each Pricing API price, so the plan they bought is the plan you unlock.
Python
from usethatapp import get_entitlement
ent = get_entitlement(access_token)
# Entitlement(entitled, version, product_public_id, status, is_free, period_end)
if ent.entitled and ent.product_public_id == "prod_<your-pro-id>":
unlock_pro_features()JavaScript
import { getEntitlement } from "usethatapp";
const ent = await getEntitlement(accessToken);
// { entitled, version, product_public_id, status, is_free, period_end }
if (ent.entitled && ent.product_public_id === "prod_<your-pro-id>") {
unlockProFeatures();
}Not entitled? Close the loop right there — render an upgrade CTA with purchase_url / purchaseUrl. Every framework guide (Python, JavaScript) shows this pattern wired into its stack.
Step 5 — Let buyers manage their subscription
Link "Manage subscription" in your app to the buyer's management page for your app — upgrades, auto-renew, and the Stripe billing portal. Build it with manage_url / manageUrl (pure, needs only UTA_CLIENT_ID); next is validated the same way as on buy links and becomes the "Back to your app" button:
import { manageUrl } from "usethatapp";
manageUrl({ next: "https://yourapp.com/account" });
// → https://www.usethatapp.com/manage/<your-client-id>/?next=https://yourapp.com/accountTest it before you launch
Open your own buy link while signed in as your developer account and the flow runs in walkthrough mode: same pages, a test banner, no Stripe calls, no charge — and it finishes by setting the developer-preview entitlement so your app sees entitled: true, status: "preview". Full instructions and a launch checklist: Test your purchase flow.
