Connect your website.
Create an invoice on your server, send your buyer to its checkout, and process the signed payment event.
Download PHP / Node / Python kit ↓
1. Create your sandbox credentials
In Integrations, generate a test API key and save a webhook destination. Use the built-in inspector first, or your public HTTPS endpoint. Copy both secrets when shown. Keep them in server environment variables, never in browser JavaScript or a public repository.
2. Create an invoice from your backend
Use integer minor units: 1499 means $14.99. USD is the only sandbox pricing currency in this release. Use one idempotency key per order attempt and reuse it when retrying the same request.
curl -X POST 'https://sellpay.cc/api/v1/sandbox/invoices' \
-H "Authorization: Bearer $SELLPAY_TEST_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: order-1001-attempt-1' \
-d '{"order_id":"ORDER-1001","amount_minor":1499,"currency":"USD","expiry_minutes":60}'
The JSON response includes id, checkout_url, status, expires_at, version and livemode: false. Redirect the buyer to checkout_url. No API key is needed to view that checkout. Creating the invoice again with the same idempotency key returns the same invoice; changed parameters return HTTP 409.
3. Simulate a payment
Open Payments in your merchant dashboard. Select an outcome and choose Simulate. Status controls are available only to the authenticated merchant, not to checkout visitors or API-key holders.
| Event | Meaning |
|---|---|
| invoice.pending | Invoice created |
| invoice.processing | Simulated payment detected |
| invoice.paid | Simulated payment confirmed |
| invoice.expired | Expired without a payment, or manually simulated expiry |
| invoice.failed | Simulated failure |
4. Verify and record the webhook
Read the exact raw request body and the Sellpay-Signature header, formatted as t=TIMESTAMP,v1=HEX_SIGNATURE. Compute HMAC-SHA256 using your webhook secret over TIMESTAMP + "." + RAW_BODY. Compare in constant time, and reject timestamps outside a five-minute window. The signature changes on retries; the event ID and payload do not.
# Python signature verification (standard library)
import hashlib, hmac, time
def verify(raw_body, signature_header, secret):
try:
fields = dict(part.split('=', 1) for part in signature_header.split(','))
timestamp = int(fields['t'])
if abs(int(time.time()) - timestamp) > 300:
return False
expected = hmac.new(secret.encode(),
str(timestamp).encode() + b'.' + raw_body,
hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, fields['v1'])
except (KeyError, ValueError):
return False
After verification, enforce livemode == false in your test receiver. Persist the event ID with a unique database constraint before returning 2xx. Duplicate events should return 2xx without repeating work. For test fulfillment, verify the invoice's order reference, currency and amount against your test order, and handle fulfillment transactionally. Do not use a browser redirect as proof of payment.
Delivery may be duplicated or arrive out of order. Use the invoice version and retrieve its current status when necessary:
curl 'https://sellpay.cc/api/v1/sandbox/invoices/INVOICE_ID' \ -H "Authorization: Bearer $SELLPAY_TEST_API_KEY"
Delivery and troubleshooting
- Any 2xx response acknowledges delivery. Other responses and connection failures retry after approximately 10 seconds, 1 minute, 5 minutes, 15 minutes and 1 hour (six automatic attempts total).
- Integrations shows the raw event, status, signature, HTTP result and latency. Replay an event to verify duplicate handling. An event allows twelve total attempts including manual retries.
- Changing the destination rotates its secret and cancels pending deliveries. Replay explicitly to send an existing event to the new destination. A request already in flight may still complete.
- HTTPS port 443 and public destination IPs are required. Local/private addresses and redirects are blocked. Use an HTTPS tunnel for a local development receiver.
- The API allows 300 authenticated requests per merchant per 15-minute window. HTTP 429 includes Retry-After. Keys grant test invoice creation and reads only. Rotate or revoke them from Integrations.
- Processing invoices do not automatically expire: this represents a payment detected before the deadline. Use Simulate to finish the test.
Three ways to connect
| Your website | Setup |
|---|---|
| Simple website | Integrations → Payment links. Set your title, USD price and checkout expiry. Copy the link or button HTML into your site. No credentials in your page. |
| WooCommerce | Download the plugin, upload it through WordPress Plugins, then configure your API key and webhook secret in WooCommerce Payments. Classic and block checkout adapters are included. |
| Custom website | Use the API and signed webhooks described above. Download server-side PHP/Node helpers and a Python test receiver below. |
Download integration kit ↓ Download WooCommerce plugin ↓
Test before going live
This release uses USD sandbox invoices only. WooCommerce is restricted to store administrators and shop managers; test on a staging store because simulated paid orders can trigger emails and downloads. Payment links create an invoice only after the visitor presses Open test checkout.
One active API key and webhook destination are shared by all integrations for each merchant. Saving a new destination replaces the previous destination. WooCommerce ignores invoices that do not belong to its orders. Independent store connections, return URLs, refunds, subscriptions and live settlement are not enabled.
Setup checklist
- Create a test invoice and open its checkout.
- Simulate processing, then paid. Confirm the matching test order updates.
- Replay the paid event and confirm the order is not fulfilled twice.
- Test failure, expiry, an invalid signature and a temporary endpoint failure.
- Keep the webhook secret and API key in your server configuration; never in button HTML.