The app talks to Vibiz with one server secret, VIBIZ_APP_KEY (already in the env on Vibiz hosting and connected hosts; the development env from vibiz_dev_env has its own). Server-side only: never in client components or NEXT_PUBLIC_ variables.
import { vibiz } from '@vibiz/app'; // reads VIBIZ_APP_KEY and VIBIZ_API_URL from the envInstall it with npm install @vibiz/app. A project that can't add a dependency calls the same HTTP routes with fetch (below), which is all it does.
Checkout
An Upgrade button → a server action or route handler → redirect.
const { url } = await vibiz.payments.checkout({
lookup_key: 'pro-monthly', // or price_id: 'pprice_…', or amount_cents + name for a one-off
user: session.user.id, // your own user id: subscriptions and payments are found by it
success_url: `${origin}/billing/success`,
cancel_url: `${origin}/pricing`, // https; http://localhost is fine in development
// customer_email?, quantity?, metadata? (≤ 20 keys, echoed on the payment and webhooks)
});
redirect(url); // the hosted page is for one buyer and expires after 1 hourNext.js server action:
'use server';
import { redirect } from 'next/navigation';
import { vibiz } from '@vibiz/app';
export async function upgrade() {
const user = await currentUser(); // your sign-in
const { url } = await vibiz.payments.checkout({
lookup_key: 'pro-monthly',
user: user.id,
success_url: 'https://app.example.com/billing/success',
cancel_url: 'https://app.example.com/pricing',
});
redirect(url);
}
// <form action={upgrade}><button>Upgrade</button></form>Create the checkout on every click: never hard-code a checkout URL, it is for one buyer and expires.
Is this user subscribed?
await vibiz.payments.hasActive(userId, 'pro-monthly'); // boolean; price id or lookup key, or omit for any plan
await vibiz.payments.subscription(userId); // { id, status, price_id, current_period_end, cancel_at_period_end } | nullManage or cancel: const { url } = await vibiz.payments.portal(userId, { return_url });
Catalog: await vibiz.payments.products(). What sold: await vibiz.payments.payments({ user }).
Refunds and withdrawals are not the app's (the app key is refused): they are the owner's.
The same over HTTP
Authorization: Bearer $VIBIZ_APP_KEY, base $VIBIZ_API_URL (default https://app.vibiz.app).
POST /v1/app/payments/checkout { lookup_key | price_id | amount_cents+name, user, success_url, cancel_url } → { url }
GET /v1/app/payments/subscriptions?user=u_123&status=active,trialing → { data: [...] }
POST /v1/app/payments/portal { user, return_url } → { url }
GET /v1/app/payments/products
GET /v1/app/payments/payments?user=const res = await fetch(`${process.env.VIBIZ_API_URL ?? 'https://app.vibiz.app'}/v1/app/payments/checkout`, {
method: 'POST',
headers: {
authorization: `Bearer ${process.env.VIBIZ_APP_KEY}`,
'content-type': 'application/json',
},
body: JSON.stringify({ lookup_key: 'pro-monthly', user: user.id, success_url, cancel_url }),
});
if (!res.ok) throw new Error((await res.json()).error.message);
const { url } = await res.json();A key works in one environment: the development key sees test mode, the production key live.