Payment Buttons

Payment buttons let you accept Monero on any website — no backend, no plugins, no JavaScript. Paste a single HTML snippet and customers can pay with one click.

Zero JavaScript. Buttons are pure HTML forms that POST directly to the Rebel Pay API. They work everywhere — even on sites with strict CSP policies or in Tor Browser's safest mode.

Quick Start

  1. Go to Dashboard → Buttons
  2. Choose a button style (Dark, Light, Orange, or Outline)
  3. Set the amount and currency
  4. Optionally add a description, customer fields, and link expiration
  5. Click Generate — copy the HTML snippet
  6. Paste it into your website

That's it. When a customer clicks the button, they're taken to a Rebel Pay payment page.

Finding Your Store ID

Payment buttons use your Store ID (not your API key) to identify your account. This is a public identifier — safe to embed in client-side HTML.

Find it at: Settings → API Keys — it's displayed at the top of the page.

Important: Never put your API key in button code or any client-side HTML. The button endpoint only requires your Store ID, which is safe to expose publicly.

Button Generator

The easiest way to create buttons is the visual generator in your dashboard. It handles all the details and gives you ready-to-paste HTML.

Options

Option Description
Style Dark, Light, Orange, or Outline — choose what fits your site
Amount Fixed payment amount (e.g. 25.00)
Currency USD, EUR, or XMR
Description What the customer is paying for (shown on the payment page)
Customer Fields Collect name, email, order ID, or custom fields before payment
Link Expiration Set when the payment link becomes invalid (hours or absolute date)

Customer Fields

Collect customer information on the payment page before or during checkout. Fields render as labeled inputs that the buyer fills out. Their responses are stored in the charge's metadata.buyer_info object and delivered in webhook payloads.

How It Works

Add a fields parameter to any payment URL with a comma-separated list of field names. Each field renders as a text input on the payment page. Field names must be lowercase alphanumeric with underscores (a-z, 0-9, _), max 30 characters each, up to 10 fields.

/pay/new?mid=YOUR_MERCHANT_ID&amount=25&currency=usd&fields=email,name

Underscores in field names are displayed as spaces with title case. For example, discord_username renders as "Discord Username" and order_id renders as "Order Id".

Common Fields

The button generator offers these presets, but you can use any name you want:

  • name — customer's full name
  • email — email address (rendered as an email input with validation)
  • order_ref — your internal order reference

Custom Fields

Use any descriptive field name. Examples:

fields=discord_username,license_key,shipping_address
fields=company_name,vat_number,purchase_order

All field values are limited to 200 characters (254 for email), HTML-stripped, and rate-limited to prevent abuse.

Where Field Data Appears

Buyer responses are available in three places:

  • Webhook payloadsmetadata.buyer_info.email, metadata.buyer_info.discord_username, etc.
  • API response — same path when fetching charge details
  • Dashboard — visible in charge detail view

Webhook Example

{
  "event": "charge.confirmed",
  "data": {
    "id": "ch_abc123...",
    "amount": 25,
    "currency": "USD",
    "status": "confirmed",
    "metadata": {
      "description": "Pro Plan",
      "buyer_fields": "email,discord_username",
      "buyer_info": {
        "email": "customer@example.com",
        "discord_username": "player42"
      }
    }
  }
}

Security

  • Field names are validated: alphanumeric + underscores only, max 30 characters
  • Values are HTML-stripped and length-limited server-side
  • Form submissions are rate-limited (10/minute per IP)
  • All button URLs are HMAC-signed — tampering with parameters returns a 403 error
  • Buyer data is automatically pruned per your data retention policy

Link Expiration

By default, payment buttons work indefinitely. You can set an expiration so the button stops accepting new payments after a certain time — useful for limited-time offers, event tickets, or flash sales.

Two ways to set expiration:

  • Hours from now — e.g. "24 hours" (the button generator calculates the absolute time)
  • Absolute date/time — e.g. "2026-03-01T00:00:00Z"

After expiration, clicking the button shows an "This payment link has expired" message instead of creating a charge.

Note: Link expiration is separate from charge timeout. Link expiration controls when the button stops accepting new clicks. Charge timeout (60 minutes) controls how long a customer has to complete a specific payment after clicking.

API Reference

If you need programmatic control — generating buttons dynamically, integrating with your own frontend, or building a custom checkout — use the button API directly.

Create Button Charge

POST /api/charges/button

Public endpoint — no API key required. Uses your Store ID instead.

Request Body:

{
  "merchant_id": "681ca94c03a7...",   // Required — from Settings → API Keys
  "amount": 25.00,                     // Required
  "currency": "USD",                   // Optional (default: USD)
  "description": "Premium Plan",       // Optional
  "fields": ["name", "email"],         // Optional — collect customer info
  "buyer_fields": {                    // Optional — pre-filled field values
    "name": "John Doe",
    "email": "john@example.com",
    "order_id": "ORD-123"
  },
  "expires_at": "2026-03-01T00:00:00Z",  // Optional — absolute expiration
  "expires_in": 24                     // Optional — hours until expiration
}

Response:

{
  "id": "ch_abc123def456",
  "status": "pending",
  "amount": 25.00,
  "currency": "USD",
  "amount_xmr": 0.071428,
  "subaddress": "84Hv16y6x7BTie3ib...",
  "pay_url": "/pay/ch_abc123def456",
  "expires_at": "2026-02-16T09:00:00.000Z",
  "created_at": "2026-02-16T08:00:00.000Z"
}

After creating the charge, redirect the customer to /pay/{charge_id} to show the payment page with QR code and Monero address.

Parameters

Parameter Type Required Description
merchant_id string Yes Your Store ID (from Settings)
amount number Yes Payment amount
currency string No USD, EUR, or XMR (default: USD)
description string No Shown on payment page
fields array No Field names to collect: ["name", "email", "order_id"] or any custom string
buyer_fields object No Pre-filled values for fields (from form submission)
expires_at string No ISO 8601 timestamp — link expires at this time
expires_in number No Hours until expiration (alternative to expires_at)

Examples

Simple Button (No Fields)

A basic "Pay $25" button — customer clicks and goes straight to payment:

curl -X POST https://your-server.com/api/charges/button \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_id": "YOUR_MERCHANT_ID",
    "amount": 25.00,
    "currency": "USD",
    "description": "T-shirt"
  }'

With Customer Fields

Collect name and email before payment:

curl -X POST https://your-server.com/api/charges/button \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_id": "YOUR_MERCHANT_ID",
    "amount": 49.99,
    "currency": "USD",
    "description": "Annual License",
    "fields": ["name", "email"],
    "buyer_fields": {
      "name": "Jane Smith",
      "email": "jane@example.com"
    }
  }'

With Custom Fields

Collect anything you need — Discord handle, license key, shipping info:

curl -X POST https://your-server.com/api/charges/button \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_id": "YOUR_MERCHANT_ID",
    "amount": 9.99,
    "currency": "USD",
    "description": "Discord Premium Role",
    "fields": ["email", "Discord Username"],
    "buyer_fields": {
      "email": "user@example.com",
      "Discord Username": "user#1234"
    }
  }'

With Expiration

Create a time-limited payment link:

curl -X POST https://your-server.com/api/charges/button \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_id": "YOUR_MERCHANT_ID",
    "amount": 199.00,
    "currency": "USD",
    "description": "Early Bird Conference Ticket",
    "fields": ["name", "email"],
    "expires_in": 48
  }'

Webhook Data

Customer field data is included in all webhook events for that charge, under data.metadata.buyer_fields:

{
  "event": "charge.confirmed",
  "data": {
    "id": "ch_abc123",
    "amount": 49.99,
    "currency": "USD",
    "status": "confirmed",
    "metadata": {
      "description": "Annual License",
      "buyer_fields": {
        "name": "Jane Smith",
        "email": "jane@example.com"
      }
    }
  }
}

Use this to fulfill orders, send confirmations, or update your CRM. See Webhooks for the full event reference.

Security

  • Store ID is public — safe to embed in HTML. It can only be used to create charges for your account; it cannot read data or perform any other actions.
  • Never embed your API key — API keys have full read/write access to your account. Keep them server-side only.
  • Rate limited — the button endpoint is rate-limited to prevent abuse.
  • Charge validation — each charge gets a unique Monero subaddress. Payments are verified on-chain with 10 confirmations.

Next Steps