Payments API v1
Charge from your own storefront.
JuzePOS merchants can take payments on a custom website, WooCommerce, or a Shopify payments app. Redirect shoppers to a hosted pay page, or charge a token you collected yourself. Catalog prices and stock stay in sync with the counter.
Step-by-step for store owners: Payments API merchant tutorial. This page is the technical reference.
Choose a path
- Hosted checkout — your server creates a checkout, you redirect the shopper to JuzePOS. You never touch card data.
- Direct charge — collect the card on your domain with Stripe.js or Square, or send wallet, cash, ACH, Tap to Pay, or a split tender.
- WooCommerce / Shopify — the storefront plugin opens a hosted checkout. Those carts already include tax.
- Marketplace — a JuzePOS-hosted storefront is Settings → Marketplace, not this API.
1. Get an API key
There is no separate developer signup. Use your existing JuzePOS merchant account (owner or manager):
- Sign in to the dashboard
- Open Settings → Developer
- Create a key. The secret (
jz_live_…) is shown once — copy it. - 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). The pay page takes a card. Tax stays $0.00 unless you send applyTax: true.
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.
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. Wallet, cash, ACH, Tap to Pay, split
Set paymentMethod on POST /v1/payments. card is the default and needs a Stripe (pm_ / pi_) or Square (cnon: / ccof:) token. Wallet needs customerId. Cash needs no token. ACH and Tap to Pay need a paymentToken; Tap to Pay also sends deviceId.
Split sends splitCashAmount and splitCardAmount. They must add up to the final total, including tax and delivery. A card portion is $0 or at least $0.50.
curl -X POST https://juzepos.com/api/v1/payments \
-H "Authorization: Bearer jz_live_..." \
-H "Content-Type: application/json" \
-d '{
"amount": 15.00,
"paymentMethod": "split",
"splitCashAmount": 10.00,
"splitCardAmount": 5.00,
"paymentToken": "pm_..."
}'6. Store tax and ProofMile
applyTax: true adds the location rate, or the merchant rate, to the goods subtotal. Delivery is not taxed. Leave it off when the amount you send already includes tax.
curl -X POST https://juzepos.com/api/v1/checkouts \
-H "Authorization: Bearer jz_live_..." \
-H "Content-Type: application/json" \
-d '{
"amount": 100.00,
"applyTax": true,
"locationId": "<location-id>",
"returnUrl": "https://your-shop.com/success",
"cancelUrl": "https://your-shop.com/cart"
}'delivery.address quotes ProofMile, adds the live fee, and dispatches after payment. The store and pickup location must already have ProofMile enabled.
curl -X POST https://juzepos.com/api/v1/checkouts \
-H "Authorization: Bearer jz_live_..." \
-H "Content-Type: application/json" \
-d '{
"amount": 42.00,
"applyTax": true,
"returnUrl": "https://your-shop.com/success",
"cancelUrl": "https://your-shop.com/cart",
"delivery": {
"address": {
"line1": "100 Main St",
"city": "Charlotte",
"state": "NC",
"postalCode": "28202"
}
}
}'7. List, retrieve, and cancel
The checkout id and payment id are the same JuzePOS order id. GET /v1/payments/:id returns a payment object (succeeded, failed, pending, refunded, canceled). GET /v1/checkouts/:id returns the same sale with hosted-checkout fields (returnUrl, requires_payment, and so on).
curl "https://juzepos.com/api/v1/payments/<checkout_or_payment_id>" \ -H "Authorization: Bearer jz_live_..."
GET /v1/payments lists API payments (status, page, limit). POST /v1/payments/:id/cancel voids one unpaid payment. POST /v1/payments/cancel voids up to 50. A paid payment is refunded, not voided.
curl -X POST https://juzepos.com/api/v1/payments/cancel \
-H "Authorization: Bearer jz_live_..." \
-H "Content-Type: application/json" \
-d '{ "ids": ["<payment-id>", "<payment-id>"] }'8. WooCommerce and Shopify
WooCommerce installs plugins/woocommerce/juzepos-gateway.php and posts the order to POST /v1/woocommerce/checkouts. Set the JuzePOS webhook URL to https://your-store.com/?wc-api=juzepos.
Shopify runs plugins/shopify/server.mjs with JUZEPOS_API_KEY and points the payments app at POST /payment. That server calls POST /v1/shopify/payment-sessions and returns the hosted checkoutUrl. Both carts already include tax. JuzePOS is not an Authorize.net gateway, and Shopify lists it only after the payments app is installed.
9. 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"
}'10. Webhooks
Register HTTPS endpoints in Settings → Developer. Events: payment.succeeded, payment.failed, refund.completed.
We POST JSON with:
X-Juze-Signature—t=<unix>,v1=<hex>X-Juze-Event— event typeX-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'));
}11. 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.
12. 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
