Payments API v1

Charge from your own storefront.

JuzePOS merchants can take card payments on a custom website or shopping cart. Redirect shoppers to a hosted pay page, or charge a card token you collected yourself. Catalog prices and stock stay in sync with the counter.

1. Get an API key

There is no separate developer signup. Use your existing JuzePOS merchant account (owner or manager):

  1. Sign in to the dashboard
  2. Open Settings → Developer
  3. Create a key. The secret (jz_live_…) is shown once — copy it.
  4. Optionally add a webhook URL to get paid / failed / refunded events.

Default scopes are payments:read, payments:write, and catalog:read.

2. Authentication

Send the secret on every /api/v1/* request:

  • Authorization: Bearer jz_live_…
  • or X-Api-Key: jz_live_…

Base URL in production: https://juzepos.com/api. Rate limit is 300 requests / minute per key.

3. Hosted checkout

POST /v1/checkouts opens an unpaid order and returns checkoutUrl. Send the customer there. After they pay, we redirect to your returnUrl with checkout_id. Cancel uses cancelUrl.

curl -X POST https://juzepos.com/api/v1/checkouts \
  -H "Authorization: Bearer jz_live_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1001" \
  -d '{
    "amount": 42.00,
    "returnUrl": "https://your-shop.com/success",
    "cancelUrl": "https://your-shop.com/cart",
    "reference": "order-1001"
  }'

Poll status with GET /v1/checkouts/:id (payments:read). Hosted checkout is card-only. Tax on API checkouts is $0.00 — send a tax-inclusive amount if you calculate tax on your site.

4. Direct charge

Collect the card on your domain using the publishable config from GET /v1/payments/config, thenPOST /v1/payments with the resulting paymentToken.

Publishable config: GET /v1/payments/config. Refunds: POST /v1/payments/:id/refunds (omit amount for a full refund).

curl -X POST https://juzepos.com/api/v1/payments \
  -H "Authorization: Bearer jz_live_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1001" \
  -d '{ "amount": 42.00, "paymentToken": "pm_...", "reference": "order-1001" }'

5. Catalog and stock

GET /v1/products and GET /v1/categories return live prices and on-hand stock. Query params: page, limit (max 100), search, categoryId.

Prefer product-backed line items so JuzePOS owns the price and decrements the same inventory as in-store:

curl -X POST https://juzepos.com/api/v1/checkouts \
  -H "Authorization: Bearer jz_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "lineItems": [{ "productId": "<id>", "variantId": "<id?>", "quantity": 1 }],
    "returnUrl": "https://your-shop.com/success",
    "cancelUrl": "https://your-shop.com/cart"
  }'

6. Webhooks

Register HTTPS endpoints in Settings → Developer. Events: payment.succeeded, payment.failed, refund.completed.

We POST JSON with:

  • X-Juze-Signaturet=<unix>,v1=<hex>
  • X-Juze-Event — event type
  • X-Juze-Event-Id

HMAC-SHA256 of `${t}.${rawBody}` using the endpoint signing secret (shown once). Verify the raw body, not a re-serialized object. Failed deliveries retry for about 8 hours.

const crypto = require('crypto');

function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(
    header.split(',').map((p) => p.split('=').map((s) => s.trim())),
  );
  const signed = `${parts.t}.${rawBody}`;
  const expected = crypto.createHmac('sha256', secret).update(signed).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(parts.v1, 'hex'), Buffer.from(expected, 'hex'));
}

7. Errors and idempotency

Send Idempotency-Key on checkout and charge. The same key within 24 hours returns the original order — it will not double-charge.

Typical HTTP codes: 400 validation / declined card, 401 missing or revoked key, 403 missing scope or suspended merchant, 404 unknown id, 429 rate limit.

8. OpenAPI reference

Interactive schema (including dashboard JWT routes) lives at /api/docs. Use the api_key scheme or X-Api-Key for /v1/* routes.

Questions: support@juzepos.com