How hosted checkout works
RMT.GG stays merchant of record. Your partner shop sends a locked amount and currency. The buyer pays on our hosted page. After payment, you deliver in your shop. Orders auto-complete so wallet settlement can run.
- The buyer pays exactly the amount and currency you send. We do not gross it up.
- Your seller fee is 4% of that locked amount. Marketplace listings stay at the usual 5%.
- Card, PayPal, and crypto processing fees are absorbed by the platform on this flow.
Allowlist required
Apply under Settings, Hosted checkout, then wait for admin approval. Unapproved API keys receive CHECKOUT_PARTNER_NOT_APPROVED.
Apply and get approved
After approval, create an API key with checkout:write and a JSON webhook for checkout.completed on the Hosted checkout settings page. Until approval, hosted checkout APIs stay blocked.
- 1Open Settings → Hosted checkout and submit your shop URL plus a short note on how you will use checkout.
- 2Wait for an admin to approve. You will see a pending badge until then.
- 3After approval, create an API key and a JSON webhook for checkout.completed on Settings → Hosted checkout.
Seller API
Use this from any shop or your own server. Create a session with POST /api/v1/checkout/sessions. Send checkout:write and an Idempotency-Key. Redirect the buyer to hosted_url. Listen for checkout.completed, then fulfill in your shop.
Create a hosted checkout session
curl -X POST -H "Authorization: Bearer rmt_sk_live_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: inv-12345" \
https://rmt.gg/api/v1/checkout/sessions \
-d @session.jsonCreate session body
{
amount: 10, // required when items is omitted
currency: "USD", // optional; defaults to USD
itemName: "Gold pack", // required when items is omitted; alias title
description: "1000 gold for account example", // optional
imageUrl: "https://cdn.shop.example/gold.png", // optional, HTTPS only
categorySlug: "games", // send with offering, or omit both
offering: "Mods", // send with categorySlug
email: "[email protected]", // optional
returnUrl: "https://your-shop.example/thanks", // optional
cancelUrl: "https://your-shop.example/cancel", // optional
invoiceId: "12345" // optional
}Multiple locked lines with optional delivery (session.json)
{
currency: "USD",
invoiceId: "12345", // optional
items: [ // required when amount is omitted
{
name: "Gold pack", // required per line
description: "1000 gold", // optional
amount: 10, // required per line
quantity: 1, // optional, default 1
imageUrl: "https://cdn.shop.example/gold.png", // optional, HTTPS only
delivery: [ // optional
{ name: "Code", type: "text", value: "XXXX-YYYY" } // name and value required; type is optional
]
}
]
}- Send amount plus itemName (or title) for a single item, or a non-empty items[] array. Currency defaults to USD. Optional fields: description, HTTPS imageUrl, email (pre-fills the pay page), returnUrl, cancelUrl, invoiceId, categorySlug plus offering (send both or neither), and metadata. itemName is the pay page heading; description is the longer copy underneath. The buyer must confirm a valid email before paying. After payment the buyer stays on the RMT.GG confirmation so they can copy delivery fields. If you set returnUrl and there are delivery fields, they continue to your shop from that screen. If there are none, we send them back after a short countdown. If you omit returnUrl or cancelUrl, the buyer stays on the RMT.GG pay page after payment, cancel, or expiry.
- On success we append the order uid to returnUrl as ?order= when that URL is HTTPS (or localhost).
- Repeat Idempotency-Key or invoiceId values reuse the same session instead of creating a duplicate.
- itemName and description appear on the RMT pay page only. Card, PayPal, and crypto checkout use catalog-coded labels like Games · Add-ons, never your shop title. Set a default under Settings, Hosted checkout, or send categorySlug and offering on the session. categorySlug is a public root slug such as games, not a child game slug. offering can be the name, slug, or coded label (Mods, Add-ons). Matching is case-insensitive. If the pair is missing one side or does not match the catalog, the API returns 400 INVALID_PSP_CATEGORY and no session is created.
- Sessions last 24 hours. After that the pay page cannot complete, in-flight Stripe and PayPal sessions are closed, and we POST checkout.canceled with reason expired.
- items[] is the multi-line alternative to a single amount plus itemName body. Each line needs name and amount. With items, amount may be omitted and is computed from the lines, or must match the sum if you send it. Quantity is locked on rmt.gg: buyers cannot add, remove, or change lines. imageUrl must be HTTPS; we do not fetch it. Optional delivery fields (text, password, or textarea) need a name and value. They are shown after payment on the confirmation page and Purchases. Public session GET includes delivery values only after payment. Seller GET and webhooks never include them.
Check payment status
Listen for checkout.completed first. If a webhook is delayed, GET the session. paid is true when the buyer has paid. You can look up by session uid or by the invoiceId you sent.
- GET /api/v1/checkout/sessions/{uid} using the uid returned when you created the session.
- GET /api/v1/checkout/sessions?invoiceId=YOUR_INVOICE_ID if you stored your own invoice id instead.
Look up by invoice id
curl -H "Authorization: Bearer rmt_sk_live_…" \
https://rmt.gg/api/v1/checkout/sessions?invoiceId=inv-12345Digiseller and similar shops
Skip this if you create checkouts with the Seller API. Use it when your shop (for example Digiseller) sends the buyer here through Setup individual payment methods. Digiseller posts to our Payment URL, we take the payment, then Digiseller checks the Status URL.
- 1Copy the Payment URL, Status URL, and payment secret from Settings → Hosted checkout.
- 2Paste them into Digiseller under Setup individual payment methods. Keep the secret private. If Digiseller generated the secret, paste that secret into Settings instead.
- 3Digiseller then sends buyers to us to pay, and checks the Status URL for paid or unpaid.
- item_name (or title) is the heading on the pay page. description is the longer copy underneath. If both names are omitted, description is used as the heading.
Example request Digiseller sends
{
"invoice_id": "12345",
"amount": "10.00",
"currency": "USD",
"item_name": "Gold pack",
"description": "1000 gold for account example",
"signature": "…"
}Prefer a JSON webhook
The Status URL is for Digiseller. If you fulfill from your own server, add a JSON webhook and listen for checkout.completed. You can also GET the session as a backup.
Hosted checkout events
On Settings → Hosted checkout, add a JSON HTTPS endpoint and subscribe to hosted checkout events. After payment we POST checkout.completed. If the buyer leaves unpaid we POST checkout.canceled. After a refund we POST checkout.refunded. Match the sale with invoiceId or checkout.uid. These are separate from marketplace order.paid events.
checkout.completed payload
{
"id": "whd_…",
"type": "checkout.completed",
"created": "2026-08-14T12:00:00.000Z",
"data": {
"checkout": {
"uid": "pcs_…",
"status": "paid",
"amount": 10,
"currency": "USD",
"itemName": "Gold pack",
"invoiceId": "inv-12345",
"source": "api",
"orderUid": "ord_…",
"items": [
{
"name": "Gold pack",
"description": "1000 gold for account example",
"amount": 10,
"quantity": 1,
"imageUrl": "https://cdn.shop.example/gold.png"
}
]
}
}
}- source is api for Seller API sessions.
- Optional signing uses X-RMT-Timestamp and X-RMT-Signature. Verify HMAC the same way as any other seller webhook.
- checkout.canceled includes reason buyer_canceled when the buyer leaves the pay page unpaid, or reason expired when the 24-hour session TTL elapses unpaid.
- checkout.items lists locked lines (name, description, amount, quantity, imageUrl). Delivery values are never included in webhooks.
Do not double-fulfill
Digiseller also gets its own payment callback. If you also listen for checkout.completed, fulfill the order only once. Seller API sessions never send that Digiseller callback.
Guest email and receipts
Buyers must enter an email on the pay page so we can send the receipt and tax invoice. Matching RMT accounts are attached without signing the buyer in.
- If the email already belongs to an RMT account, the order appears in that account's Purchases. No login cookie is set on the pay page.
- A new email creates a lightweight user so receipts and later sign-in still work.
- Email is required to complete payment. Your shop should still listen for checkout.completed.
Currencies and limits
Use a supported ISO currency such as USD or EUR. Card checkout works best in USD or EUR. Crypto and PayPal can still work for other currencies when the provider accepts them.
Not in this version
There is no iframe SDK, custom pay-page domain, or wallet balance on the hosted pay page. Buyers use card, PayPal, or crypto redirect checkout.
Before you go live
Staging checklist
Unit tests cover fees, HMAC, and reuse. Walk through this list on staging before you send real buyers.
- Apply, confirm the pending badge, then have an admin approve. Credentials appear only when approved. An unapproved key on POST /api/v1/checkout/sessions returns 403.
- Create a session with itemName, description, and categorySlug plus offering (or a saved default). Open hosted_url. The heading is the item name; the description is underneath. The pay page has no top nav or sidebar. Pay with card in USD or EUR. Card, PayPal, and crypto pages should show a catalog label such as Games · Add-ons, not your shop title.
- Confirm checkout.completed includes invoiceId, checkout.uid, and source, then fulfill in your shop. Cancel the pay page and confirm checkout.canceled with reason buyer_canceled. Let a session sit past 24 hours and confirm checkout.canceled with reason expired cannot still be paid.
- For Digiseller, paste the Payment URL, Payment status URL, and payment secret into Setup individual payment methods, or paste Digiseller's generated secret into Settings. Confirm Digiseller can see paid status, and that you fulfill the order only once.
- Pay with an existing RMT email: the order lands in Purchases and a receipt is sent, with no login cookie. A new email creates a lightweight user. Email is required on the pay page.
- The hidden synthetic offer must not appear in marketplace search. On a waitlist-gated host, /pay/:uid and adapter routes still work.
- Send optional imageUrl and items[] if you need a product image or multiple locked lines. Confirm the pay page has no quantity controls. After payment, delivery fields appear on Purchases and the order page. HTTPS image URLs only.
Related guides
Apply for hosted checkout
Open Settings → Hosted checkout to apply. After approval, create an API key and a checkout.completed webhook on that same page.