Your customer enters their email at checkout, BrokkrPay emails them a secure payment link on your store's behalf, they pay on a hosted page and return to your store. Get notified by webhook, then swap the test token for live.
Your customer enters their email at checkout, BrokkrPay emails them a secure payment link on your store's behalf, they pay on a hosted page and return to your store. The examples below use a placeholder test token; nothing is charged.
1. Email a payment link
We email it to your customer
2. Customer pays
On the hosted payment page
3. Back to your store
Returned to your returnUrl
4. Order confirmed
Webhook completes the checkout
Each site has its own test and live token. Sign in to your BrokkrPay dashboard to copy your real token — the placeholder below shows the format.
The token is a server-side secret — it creates real payment links. Keep it out of browser code and version control.
Collect the customer's email in your checkout, then make one authenticated POST with customerEmail — BrokkrPay creates the hosted payment page and emails the customer the link.
The response is ready to hand to your frontend:
| Body field | Requirement | Description |
|---|---|---|
| amount | Required | Order total in whole currency units — not cents. 180 charges $180.00. Decimals are rejected. |
| currency | Required | ISO currency code. Currently USD. |
| customerEmail | Required | Where BrokkrPay emails the payment link. The link is delivered by email and nowhere else, so without this the customer has no way to reach the payment page. |
| returnUrl | Recommended | Your checkout page. Once the customer finishes on the hosted payment page they are sent here with ?orderId=…&status=success (or status=cancelled), so they land back in your store. Stored exactly as you write it — hash-routed single-page apps need a path-based URL, see step 3. |
| reference | Recommended | Your own order or cart ID, up to 200 characters. Echoed back as reference in every webhook for this order. Not required to be unique — we never deduplicate on it, and an order created without one gets reference: null. |
amount is whole currency units, not cents
Most processors take minor units. We do not. 180 charges $180.00. If you send 18000 expecting $180.00 you will charge $18,000 — that is a valid amount and we will accept it.
Which amounts are accepted
Your customer pays on a hosted page whose line items come from our storefront’s product catalog — you never register products with BrokkrPay. So the total has to be reachable as a sum of the prices in that catalog (repeats allowed). It is dense at ordinary retail amounts, so in practice this only bites on unusual totals; when it does, creation fails with a 400 naming the amount, before the customer ever sees a payment page. Round to a nearby amount, or ask us to add the price point.
BrokkrPay emails the link to customerEmail — that is the only way it is delivered. When the customer finishes paying, they are returned to the returnUrl you set, back inside your own store.
The payment link is email-only
The API response never contains the checkout URL, so there is nothing to surface in your UI or open in a tab — customers reach the payment page from the email BrokkrPay sends them, which carries the disclosure about how the purchase appears on their statement.
If the email doesn’t arrive
Because the link is email-only, a bounced or mistyped address strands the customer. Two things to handle: customerEmailSent: false in the create response means the send failed outright — tell the customer at once rather than showing “check your email”. And a send that succeeded can still land in spam. Either way, resend:
Works while the order is PENDING and the link is unexpired; pass a corrected customerEmail to change the recipient. Once the link has expired, create a new payment link instead. Your support team can do the same from the Orders table in your dashboard, without touching code.
The return trip goes through our storefront and out again. Stripe hands the customer to the storefront, which immediately forwards them to your returnUrl — they only pass through it, they never browse it.
Both outcomes come back to the same URL, distinguished by the status param:
| Paid | …/checkout/complete?orderId=<uuid>&status=success |
| Cancelled | …/checkout/complete?orderId=<uuid>&status=cancelled |
Treat status as a display hint only — the webhook in the next step is what tells you an order is paid. A cancelled payment leaves the order PENDING and the link still valid, so the customer can reopen it from their email and finish. Links expire after 24 hours, at which point the order becomes CANCELLED.
Single-page apps: use a path, not a hash
We append ?orderId=…&status=… to your returnUrl exactly as you wrote it. If your URL contains a #, the query string ends up inside the fragment where location.search can’t read it. Put your own reference in the path and redirect server-side:
A real endpoint, not just a fallback: your status page needs it whenever a webhook is delayed, and support needs it to answer 'did this actually go through?'.
GET https://api.brokkrpay.com/api/orders/<orderId> — authenticated with the same site token as everything else. The token scopes the lookup, so it only ever returns your own orders.
Responds with:
Keep the orderId ↔ reference mapping
This endpoint is keyed by our orderId, while your own records are keyed by your reference. Store the two against each other when the create call returns — without it you can receive a webhook you cannot look up, or hold a reference you cannot query.
Never call this from the browser — the token is a server-side secret. Proxy it from your own server, and poll no more than once every few seconds per order. It is a supplement to the webhook, not a replacement: the webhook is still what tells you to fulfill.
BrokkrPay POSTs to your server on every order state change — this is how you reliably fulfill orders. Save your endpoint URLs per site in your BrokkrPay dashboard.
Saved per site, and per environment: you configure a separate test and live endpoint. Test orders only ever reach the test endpoint and live orders only ever reach the live one, so a test payment can never trigger a real fulfillment. Point both at the same URL if you would rather branch on the mode field yourself. Your dashboard has a Send test event button and a delivery log showing the status each attempt returned.
Every state change arrives as a JSON POST:
Handle it and acknowledge quickly (within 5 seconds):
Order states
Ordering: a webhook can arrive before the create call returns
The first PENDING event is emitted as soon as the order exists — which is before POST /api/payment-links has finished responding to you. If your handler persists the order only after that response, there is a window in which an arriving event refers to an order you have never heard of. Usually that is the harmless PENDING, but a fast payer or a slow response can make it SUCCESS. Persist your order before calling us, and buffer events for unknown orderIds for a few seconds rather than dropping them.
Retries and acknowledgement
Reply within 5 seconds. We treat any 2xx as delivered and do not currently retry — a non-2xx, a timeout or a connection failure is recorded in your dashboard’s delivery log and not sent again. So acknowledge first and do your fulfillment work after, and use the delivery log (plus GET /api/orders/:orderId) to catch anything your endpoint missed while it was down.
Security: webhooks are unsigned, so use a long, unguessable URL. Test and live orders go to separate endpoints, but still check the mode field (test or live) as a second line of defence. Treat the webhook — never the customer returning to your site — as the signal to fulfill.
Run an end-to-end payment in test mode. Going live needs BrokkrPay approval.
Test card
4242 4242 4242 4242
Any future expiry, any CVC. Test orders never charge a real card.
Going live
Once BrokkrPay approves your site for live payments, switch to Live and swap your bpk_test_… token for the bpk_live_… token on the same site. Everything else stays identical.
Prefer to explore first? Your BrokkrPay dashboard has a demo playground that creates a real payment link with live webhook delivery, no code required.
Tell us your vertical and your monthly volume. Approved operators are live on their own Stripe account within a day.