Pagamento aprovado
Seu pagamento foi confirmado.
Código do pagamento
Guarde este código: com ele a loja encontra seu pagamento.
Credit cards from customers in any country and Pix from Brazil, in one secure checkout. Withdraw by Pix or in USDT, wherever you are.
Enter a valid email, like [email protected].
Withdraw in USDTNo monthly fee
Our checkout runs on modern payment technology designed to raise your conversion rate. When a card is declined, the sale doesn't end: your customer can retry with another card or switch to Pix in one click, without leaving the page.
Your customers pay by card or Pix. You choose how to take the money out: by Pix in Brazil, or in USDT straight to your crypto wallet, from any country.
Your customers pay with confidence, and you never have to handle their card data.
Customers enter their card on our checkout over an encrypted connection. Your servers never see or store card numbers.
Repeated failed card attempts on a checkout are stopped automatically, so fraudsters can't use your store to test stolen cards.
Every payment notice sent to your system is signed. No one can fake a paid order, and missed notices are resent for 3 days.
One checkout for cards and Pix. Send customers from your site or share the link by chat or email.
Accept credit cards from customers in any country, charged in a single payment. They pay in reais and their bank converts the amount.
Brazil's instant payment. The customer scans a QR code in their bank app and pays in seconds.
Take your balance out in USDT to your own wallet, from anywhere in the world.
A modern payment infrastructure built to recover declined payments and turn more checkouts into revenue, automatically.
One API call creates the checkout. You get notified when it's paid. Read the docs
curl https://api.payzoi.com/v1/charges \ -H "Authorization: Bearer sk_test_•••" \ -H "Content-Type: application/json" \ -d '{ "value": 5000, "description": "Order #123", "return_url": "https://yourstore.com/thanks", "primary_color": "#2446F5" }' # → { "checkout_url": "https://pay.payzoi.com/c/ch_test_a1B2…" }
No monthly fee, no setup fee. A fee on each approved payment, and nothing else.
per approved Pix
per approved card payment
Withdraw by Pix or in USDT. High volume? Talk to us about custom rates.
Sign up and tell us about your business. Your test key works right away, so you can build and test your integration.
Our team reviews your business details. Once approved, your live key is ready to take real payments.
Sell through your checkout, then withdraw by Pix or in USDT to your wallet.
Seu pagamento foi confirmado.
Código do pagamento
Guarde este código: com ele a loja encontra seu pagamento.
Create your account. We'll email you a link to confirm your address.
The passwords don't match.
Already have an account? Sign in
Enter the email of your account. We'll send you a link to choose a new password.
After saving, every open session is signed out. Then sign in with the new password.
After approval
Check your inbox. We sent a link to . Open it to confirm your email and continue. It can take a minute, and it may land in the spam folder.
Tell us about your company. It takes about a minute, and test mode turns on as soon as you send it.
Showing test payments. The test balance is what they add after fees. Test money isn't real and can't be withdrawn.
| Date | Order | Method | Status | Amount |
|---|
| Date | Order | Customer | Method | Status | Amount |
|---|
Showing your latest 100 payments. To find an older one, use the API (List charges, with dates) or ask us under Support.
To refund a customer, open Transactions and click Refund on the payment. We return the money to the customer and take the amount from your balance; our fee on the original sale isn't returned. Your system receives a charge.refunded webhook.
| Requested | Order | Status | Reason | Amount |
|---|
A dispute (chargeback) happens when a customer asks their bank to reverse a card payment. The amount is held from your balance while it's open. Send your side before the deadline: it's what the bank uses to decide.
| Opened | Order | Reason | Respond by | Status | Amount |
|---|
Receipts or screenshots: put links to them in your response, or open a ticket under Support mentioning this order.
Withdrawals are handled by the PayZoi team. Your balance is shown in Overview; if you have questions about it, open a ticket in Support.
Available to withdraw: —. We send the whole available balance.
Your balance is converted from reais to USDT on the day the withdrawal is paid. Check the network: USDT sent on the wrong network can be lost.
| Date | Amount | To | Status | Reference |
|---|
Talk to our team about payments, withdrawals, integration or your account. We answer here. The number next to Support shows how many tickets are waiting for your reply.
| Subject | Topic | Status | Last update |
|---|
To change your business name or email, contact support.
Other devices are signed out. You stay signed in here.
Password changed.
Use these keys to integrate. Keep them secret, on your server.
The live key and the webhook secret ask for your password. A revealed key is hidden again after 1 minute.
Enter the URL on your site that will receive the payment notification.
Create a charge, send your customer to the checkout link, and we tell your server when it's paid. The customer pays on the PayZoi checkout by Pix, or by card when cards are enabled for your account, so payment data never touches your server.
checkout_url.checkout_url. They choose how to pay among the methods available and pay.charge.paid webhook to your server. If you set a return_url, we also send the customer back to your site.Values are integers in cents: 5000 is R$ 50,00. All charges are in Brazilian reais (brl), for card and Pix alike. Dates are in UTC, in ISO 8601 format. Customers with cards issued in other countries also pay in reais; their bank converts the amount to their currency.
charge.paid, or after checking the charge with Get a charge. The customer can close the browser before returning to your site.To see every step working together, go to Complete example: a small store in one file that you can run with your test key.
Send your secret key in every request:
Authorization: Bearer sk_test_your_secret_key Content-Type: application/json Accept: application/json
Base URL:
Get your keys in Dashboard → Integration. You only need the secret key: there is no public key in this integration. The key decides the mode. Requests and responses have the same format in both modes; in live mode, the payment methods available depend on your account.
sk_test_…test key | Test mode: card and Pix work end to end, with webhooks and a test balance, and no real money moves. See Testing. |
sk_live_…live key | Real payments, after your account is activated for live mode. |
valuerequiredinteger | Value in cents. Minimum 50 (R$ 0,50). Live Pix charges need at least 500 (R$ 5,00). |
descriptionstring | What the customer is paying for, shown on the checkout. Up to 255 characters. |
payment_methodsarray | "credit_card", "pix" or both. Default both; the customer chooses one. With a test key, card and Pix are always available. With a live key, only the methods enabled for your account are offered. Cards are charged in a single payment (no installments). |
expires_ininteger | Seconds until the charge expires, 300 to 172800. Default 3600 (1 hour). |
return_urlstring | Where we send the customer after paying, or when they leave the checkout. Without it, a customer who paid stays on our confirmation page, and “Back to your store” returns to the page they came from (it's hidden when there's no such page). |
webhook_urlstring | Public https URL for this charge's webhooks. Default: the URL in your dashboard. |
external_idstring | Your order ID, up to 100 characters. Returned in webhooks and on the return URL. |
customerobject | Optional name, email and document (CPF or CNPJ). Name and document fill in the checkout: Pix needs a name and a CPF or CNPJ, and if you don't send them, the customer types them. The checkout doesn't ask for an email: send it here to keep it with the payment (it comes back in payer.email). |
metadataobject | Up to 20 pairs for your own use, like UTM parameters: keys up to 40 characters, string values up to 500. Returned in webhooks. |
logo_urlstring | Your logo on the checkout, https only. Square works best. |
primary_colorstring | Your brand color for the checkout buttons, as a 6-digit hex color like #0A7C66. |
value on your server, from the order total in your database. Never take it from your page or form: the customer can change anything the browser sends.Send an Idempotency-Key header with a unique value per order, like your order ID. If a request times out and you retry with the same key within 24 hours, you get the charge created the first time (status 200 and the header Idempotent-Replayed: true) instead of a second charge.
Idempotency-Key: order-123
curl https://api.payzoi.com/v1/charges \ -H "Authorization: Bearer sk_test_your_secret_key" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: order-123" \ -d '{ "value": 5000, "description": "Order #123", "payment_methods": ["credit_card", "pix"], "external_id": "123", "return_url": "https://yourstore.com/thanks", "webhook_url": "https://yourstore.com/webhooks/payzoi", "metadata": { "utm_source": "google" } }'
// Node.js 18+ (runs on your server). Works in CommonJS and ES modules. async function createCharge() { const res = await fetch("https://api.payzoi.com/v1/charges", { method: "POST", headers: { "Authorization": "Bearer " + process.env.PAYZOI_SECRET_KEY, "Content-Type": "application/json", "Idempotency-Key": "order-123" }, body: JSON.stringify({ value: 5000, description: "Order #123", external_id: "123", return_url: "https://yourstore.com/thanks" }) }); const charge = await res.json().catch(() => null); // null if the body isn't JSON if (!res.ok) throw new Error(charge?.error?.message || `HTTP ${res.status}`); return charge; // redirect your customer to charge.checkout_url }
<?php $ch = curl_init("https://api.payzoi.com/v1/charges"); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("PAYZOI_SECRET_KEY"), "Content-Type: application/json", "Idempotency-Key: order-123" ], CURLOPT_POSTFIELDS => json_encode([ "value" => 5000, "description" => "Order #123", "external_id" => "123", "return_url" => "https://yourstore.com/thanks" ]) ]); $charge = json_decode(curl_exec($ch), true); if (!in_array(curl_getinfo($ch, CURLINFO_HTTP_CODE), [200, 201])) { // 200: same Idempotency-Key, the charge created before exit($charge["error"]["message"]); } header("Location: " . $charge["checkout_url"]);
{
"id": "ch_test_Svy9MIb066RlzlC",
"status": "pending",
"value": 5000,
"paid_value": null,
"fee": null,
"net_value": null,
"description": "Order #123",
"external_id": "123",
"payment_methods": ["credit_card", "pix"],
"logo_url": null,
"primary_color": null,
"payment_method": null,
"payment": null,
"card": null,
"last_decline_reason": null,
"refunded_value": 0,
"dispute": null,
"customer": null,
"payer": null,
"checkout_url": "https://pay.payzoi.com/c/ch_test_Svy9MIb066RlzlC",
"return_url": "https://yourstore.com/thanks",
"webhook_url": "https://yourstore.com/webhooks/payzoi",
"metadata": { "utm_source": "google" },
"livemode": false,
"created_at": "2026-09-24T16:07:30.946Z",
"expires_at": "2026-09-24T17:07:30.946Z",
"paid_at": null
}customer is what you sent in the request (or null). payer is who actually paid, filled in when the charge is paid. paid_value is what the customer paid, the same as value. When paid, payment identifies the payment itself: { "id": "py_…", "code": "PZ-7K3M-9Q2X", "acquirer_reference": "…" }. The code is also shown to the customer on the confirmation screen, so when a customer contacts you about a payment, search for it with payment_code in List charges or in your dashboard. acquirer_reference is the acquirer's own ID for this payment (for Pix, the ID at the Pix provider). Give it to support if you need to trace a payment. refunded_value is how much was refunded, and dispute is null or { "status": "open" | "won" | "lost", "amount", "reason", "respond_by", "opened_at", "closed_at" }. When the charge is paid, fee is our fee and net_value is what goes to your balance (paid_value minus fee). payment_methods in the response is what the checkout will actually offer.
Save the id with your order. If the customer comes back after the charge expires, create a new one.
"credit_card" only returns 403 card_payments_unavailable; asking for both creates the charge with Pix only. A live Pix charge below R$ 5,00 returns 422 amount_too_low.Redirect the customer to checkout_url, or send it as a link by email or WhatsApp.
| Card | The customer enters the card, charged in a single payment. If the card is declined, they can try another card or Pix. |
| Pix | We show a QR code and a copy-and-paste code, valid for 15 minutes (never past the charge's expires_at). The page updates by itself when the payment is confirmed. While a code is valid, opening the checkout again shows the same code and card payment is blocked, so the customer can't pay twice. |
| Paid | We show a confirmation and, after 5 seconds, send the customer to your return_url. |
| Canceled | “Back to your store”, at the top left, sends the customer to your return_url. Without one, it goes back to the page they came from. The charge stays pending: the customer can open checkout_url again and pay until it expires. |
We add these query parameters to your return_url:
https://yourstore.com/thanks?charge_id=ch_test_Svy9MIb066RlzlC&external_id=123&status=paid https://yourstore.com/thanks?charge_id=ch_test_Svy9MIb066RlzlC&external_id=123&status=canceled
status=canceled only says the customer left the checkout; it isn't a charge status. Anyone can type these parameters, so use them only to show a message. Confirm the payment with the webhook or Get a charge.
We send a POST with a JSON body to the charge's webhook_url, or to the URL in Dashboard → Integration if the charge doesn't have one.
charge.paid | The payment was confirmed. Release the order. |
charge.payment_failed | A card was declined. The charge stays pending and the customer can try again, so don't cancel the order. |
charge.expired | The charge expired without a payment. This is final: the charge can't be paid anymore, so you can cancel the order. |
charge.canceled | You canceled the charge with Cancel a charge. Final, like charge.expired. |
charge.refunded | A refund of a paid charge was completed. refunded_value is the total refunded so far; it can be less than paid_value for a partial refund. |
charge.dispute_opened | The customer disputed a card payment with their bank. The amount is held from your balance. Consider holding the delivery while it's open. |
charge.dispute_won / charge.dispute_lost | The dispute was decided. Won: the money is back in your balance. Lost: it stays out. |
data is the full charge, the same object you get from Get a charge:
{
"id": "evt_7TxvAlzfTmDMGIg",
"event": "charge.paid",
"created_at": "2026-09-24T16:07:32.346Z",
"data": {
"id": "ch_test_Svy9MIb066RlzlC",
"status": "paid",
"value": 5000,
"paid_value": 5000,
"fee": 450,
"net_value": 4550,
"external_id": "123",
"payment_method": "credit_card",
"payment": { "id": "py_Qm3kP0aZt8uX", "code": "PZ-7K3M-9Q2X", "acquirer_reference": "AUTH-481516" },
"card": { "brand": "visa", "last4": "4242" },
"payer": { "name": "Ana Souza", "email": "[email protected]", "document": null },
"paid_at": "2026-09-24T16:07:32.345Z",
...
}
}For Pix, payment_method is pix, card is null, and payer has the name and CPF or CNPJ. payer.email is the email you sent in customer, or null. For charge.payment_failed, last_decline_reason is card_declined, insufficient_funds, expired_card or processing_error.
Reply with any 2xx status within 10 seconds. Otherwise we retry, with growing intervals, for up to 3 days. Each attempt is signed again at the moment it's sent, so retries pass the 5-minute check below. The same event can arrive more than once, so skip events whose id you've already processed.
Pix is usually confirmed within seconds of the payment. If the customer generated a Pix code, we only send charge.expired after that code can no longer be paid and its confirmation window has closed, so a charge.paid never arrives after charge.expired or charge.canceled.
Every webhook has a PayZoi-Signature header, so you know it came from us:
PayZoi-Signature: t=1790266052,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
t (Unix time in seconds) and v1 from the header.t + "." + raw request body with your webhook secret (in Dashboard → Integration), as hex.v1 and t is less than 5 minutes old. t is the time of this delivery attempt, not of the payment.Use the raw body exactly as received. Parsing and re-encoding the JSON changes it and breaks the signature. In Express, declare the webhook route before app.use(express.json()), or that parser reads the body first.
// Express. express.raw keeps the body exactly as received. const crypto = require("crypto"); app.post("/webhooks/payzoi", express.raw({ type: "application/json" }), (req, res) => { const header = req.get("PayZoi-Signature") || ""; const parts = Object.fromEntries(header.split(",").map(p => p.split("="))); const expected = crypto .createHmac("sha256", process.env.PAYZOI_WEBHOOK_SECRET) .update(`${parts.t}.${req.body}`) .digest("hex"); const received = String(parts.v1 || ""); const valid = received.length === expected.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received)); const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300; if (!valid || !fresh) return res.status(400).send("Invalid signature"); const { id, event, data } = JSON.parse(req.body); // Skip if you already processed this event id if (event === "charge.paid") { // markOrderPaid(data.external_id, data.paid_value) } else if (event === "charge.expired") { // cancelOrder(data.external_id) } res.sendStatus(200); });
<?php $payload = file_get_contents("php://input"); $header = $_SERVER["HTTP_PAYZOI_SIGNATURE"] ?? ""; parse_str(str_replace(",", "&", $header), $parts); $t = $parts["t"] ?? ""; $expected = hash_hmac("sha256", $t . "." . $payload, getenv("PAYZOI_WEBHOOK_SECRET")); $fresh = abs(time() - (int)$t) < 300; if (!hash_equals($expected, $parts["v1"] ?? "") || !$fresh) { http_response_code(400); exit("Invalid signature"); } $event = json_decode($payload, true); // Skip if you already processed $event["id"] if ($event["event"] === "charge.paid") { // markOrderPaid($event["data"]["external_id"], $event["data"]["paid_value"]); } elseif ($event["event"] === "charge.expired") { // cancelOrder($event["data"]["external_id"]); } http_response_code(200);
A store in one file, with Node.js and Express: a product page, the purchase, the checkout and the webhook. It covers what a real integration needs:
external_id and as the Idempotency-Key.charge.paid webhook, or by Get a charge, and only when paid_value equals the order total.// store.js: a complete store with PayZoi, in one file. Node.js 18+ and Express. // npm install express // PAYZOI_SECRET_KEY=sk_test_... PAYZOI_WEBHOOK_SECRET=whsec_... STORE_URL=https://yourstore.com node store.js const express = require("express"); const crypto = require("crypto"); const API = "https://api.payzoi.com/v1"; const STORE_URL = process.env.STORE_URL; // your site's public address const app = express(); // Your catalog, with prices in cents. The price always comes from here, never from the browser. const PRODUCTS = { "blue-tshirt": { name: "Blue T-shirt", price: 7990 }, "coffee-mug": { name: "Coffee mug", price: 3450 } }; const orders = new Map(); // use your database const seenEvents = new Set(); // also in your database async function payzoi(path, options = {}) { const res = await fetch(API + path, { ...options, headers: { "Authorization": "Bearer " + process.env.PAYZOI_SECRET_KEY, "Content-Type": "application/json", ...options.headers } }); const body = await res.json().catch(() => null); if (!res.ok) throw new Error(body?.error?.message || `PayZoi returned HTTP ${res.status}`); return body; } // Marks the order paid only if the charge is paid with the order's full total. function settle(order, charge) { if (charge.status === "paid" && charge.paid_value === order.total) order.status = "paid"; if (["expired", "canceled"].includes(charge.status) && order.status !== "paid") order.status = "canceled"; } // 1. Webhook. Declared before any JSON body parser: the signature needs the raw body. app.post("/webhooks/payzoi", express.raw({ type: "application/json" }), (req, res) => { const header = req.get("PayZoi-Signature") || ""; const parts = Object.fromEntries(header.split(",").map(p => p.split("="))); const expected = crypto .createHmac("sha256", process.env.PAYZOI_WEBHOOK_SECRET) .update(`${parts.t}.${req.body}`) .digest("hex"); const received = String(parts.v1 || ""); const valid = received.length === expected.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received)); const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300; if (!valid || !fresh) return res.status(400).send("Invalid signature"); const { id, data } = JSON.parse(req.body); if (!seenEvents.has(id)) { seenEvents.add(id); const order = orders.get(data.external_id); if (order) settle(order, data); } res.sendStatus(200); }); app.use(express.urlencoded({ extended: false })); // 2. Product page: each button sends only the product ID. app.get("/", (req, res) => { const items = Object.entries(PRODUCTS).map(([id, p]) => `<form method="post" action="/buy"> <b>${p.name}</b> R$ ${(p.price / 100).toFixed(2).replace(".", ",")} <button name="product" value="${id}">Buy</button> </form>`).join(""); res.send(`<h1>My store</h1>${items}`); }); // 3. Buy: create the order, then a charge for its total, and send the customer to pay. app.post("/buy", async (req, res) => { const product = PRODUCTS[req.body.product]; if (!product) return res.status(404).send("Product not found"); const orderId = crypto.randomUUID(); const order = { product: req.body.product, total: product.price, status: "pending" }; orders.set(orderId, order); try { const charge = await payzoi("/charges", { method: "POST", headers: { "Idempotency-Key": orderId }, body: JSON.stringify({ value: order.total, description: product.name, external_id: orderId, return_url: `${STORE_URL}/thanks`, webhook_url: `${STORE_URL}/webhooks/payzoi` }) }); order.chargeId = charge.id; order.checkoutUrl = charge.checkout_url; res.redirect(303, charge.checkout_url); } catch (err) { console.error(err); res.status(502).send("We couldn't start the payment. Try again in a moment."); } }); // 4. Back from the checkout. If the webhook hasn't arrived yet, ask PayZoi for the charge. app.get("/thanks", async (req, res) => { const order = orders.get(String(req.query.external_id)); if (!order) return res.redirect("/"); if (order.status === "pending" && order.chargeId) { try { settle(order, await payzoi(`/charges/${order.chargeId}`)); } catch (err) { console.error(err); } } const name = PRODUCTS[order.product].name; if (order.status === "paid") return res.send(`<h1>Thank you!</h1><p>Your ${name} is paid.</p>`); if (order.status === "canceled") return res.send(`<h1>Order canceled</h1><p><a href="/">Back to the store</a></p>`); if (req.query.status === "canceled") { return res.send(`<h1>Payment not finished</h1><p><a href="${order.checkoutUrl}">Go back to the payment</a></p>`); } res.send(`<h1>Waiting for payment</h1><p>If you already paid, refresh this page in a few seconds.</p>`); }); app.listen(process.env.PORT || 4000);
store.js and run npm install express.https address of your site in STORE_URL. On your computer, a tunnel such as cloudflared tunnel --url http://localhost:4000 gives you one./thanks with the order paid, and the sale shows in your dashboard.To go live, swap in your sk_live_ key once your account is activated for live payments. Nothing else changes.
curl https://api.payzoi.com/v1/charges/ch_test_Svy9MIb066RlzlC \
-H "Authorization: Bearer sk_test_your_secret_key"Returns the same object as Create a charge, with the current status:
pending | Waiting for payment. If a card was declined, last_decline_reason says why. |
paid | Paid. paid_value, payment_method ("credit_card" or "pix"), payer and paid_at are filled in. For card payments, card has the brand and last4; for Pix it's null. |
expired | Expired without a payment. Final. |
canceled | You canceled it. Final. |
A charge only moves from pending to one of the other three, and never changes after that. A customer leaving the checkout doesn't change the status; the charge stays pending until it's paid, expires or you cancel it.
statusquery | pending, paid, expired or canceled. |
external_idquery | Only charges with this external_id, to find the charges of an order. |
payment_codequery | The charge paid with this payment code, like PZ-7K3M-9Q2X (the code the customer sees). |
created_fromquery | Created on or after this date, in UTC, like 2026-09-01 or 2026-09-01T12:00:00Z. |
created_toquery | Created on or before this date, in UTC. A date without time includes the whole day (until 23:59:59 UTC). |
pagequery | Page number. Default 1. |
per_pagequery | 1 to 100. Default 20. |
curl "https://api.payzoi.com/v1/charges?status=paid&created_from=2026-09-01&per_page=50" \ -H "Authorization: Bearer sk_test_your_secret_key"
{
"data": [ { "id": "ch_test_Svy9MIb066RlzlC", "status": "paid", ... } ],
"page": 1,
"per_page": 50,
"total": 1
}Newest first. Test and live charges are listed separately, according to the key. To page through a period without charges shifting between pages, set created_to to the moment you start and keep it on every page.
Use it when the order is canceled on your side, so the customer can't pay it anymore. The charge becomes canceled and we send charge.canceled. Canceling a charge that is already canceled returns it again.
curl -X POST https://api.payzoi.com/v1/charges/ch_test_Svy9MIb066RlzlC/cancel \
-H "Authorization: Bearer sk_test_your_secret_key"409 charge_paid | The charge is already paid. |
409 charge_expired | The charge already expired. |
409 pix_pending | The customer has a Pix code that can still be paid. The message says until when; try again after that. |
409 payment_in_progress | The customer is paying this charge right now. Try again in a minute. |
Refunds and disputes are handled in your dashboard, not through the API. To refund, open Transactions and click Refund on the payment (full or partial). When a customer disputes a card payment with their bank (a chargeback), it shows up under Disputes, where you send your side. Your system is told about both by webhook: charge.refunded, charge.dispute_opened, charge.dispute_won and charge.dispute_lost, with refunded_value and dispute filled in on the charge.
{ "available": 128400, "pending": 45900, "in_withdrawal": 0, "currency": "brl", "livemode": true }available | Your balance available for transfer, after fees and minus refunds in progress. It can be negative after a refund or a lost dispute; your next sales cover it. |
pending | Paid card sales, after fees, still inside the holding period set for your account. They move to available when it ends. Pix sales go straight to available. |
in_withdrawal | Transfers in progress. Already taken out of available. |
in_refund | Refunds requested and not completed yet. Already taken out of available. |
With a live key, these are your real balances. With a test key you get the test balance: your test payments after fees, minus test refunds, with livemode: false. Test sales count right away (pending is always 0), and test money can never be withdrawn. Transfers of your real balance are arranged with PayZoi support.
{
"id": "acct_0VpcprpbriM",
"name": "Northwind Studio",
"email": "[email protected]",
"status": "approved",
"live_enabled": true,
"webhook_url": "https://yourstore.com/webhooks/payzoi",
"livemode": false
}Your sk_test_ key works as soon as you send your business details in the dashboard, under Pending. Test mode behaves like live mode: same endpoints, same checkout, same webhooks and statuses. Test payments never reach a bank and no real money moves.
On a test checkout the card fields are locked and the PayZoi test card is shown above them. Tap it: it fills in 4242 4242 4242 4242, expiry 12/30 and CVC 123, and Pay completes the test purchase, approved.
Generate the code with any valid CPF or CNPJ, like 123.456.789-09. The test code can't be paid in a bank app: it's marked as paid by itself about 20 seconds after it's generated, or right away with Simulate the payment on the checkout.
charge.paid webhook, signed with your webhook secret.charge.refunded.On the Pix tab of a test checkout, Fill with test data fills in a name and a test CPF.
Errors return an HTTP status and a JSON body with a code and a message that says how to fix the request.
400 invalid_json | The body isn't valid JSON. |
401 unauthorized | The secret key is missing or not valid. |
403 account_pending | Your account setup isn't finished. Send your business details in the dashboard, under Pending: test mode turns on right away. Applies to test and live keys. |
403 account_rejected | Your account isn't authorized to process payments. |
403 account_suspended | Your account is suspended. Contact support. |
403 live_mode_disabled | Live payments aren't enabled for your account yet. Keep using your test key. |
403 card_payments_unavailable | You asked for card only, but cards aren't enabled for live payments on your account. Include "pix". |
404 not_found | No charge with this ID for this key (test and live charges are separate), or the endpoint doesn't exist. |
413 payload_too_large | The request body is over 100 kB. |
409 charge_paid, charge_expired, pix_pending, payment_in_progress | The charge can't be canceled now. See Cancel a charge. |
422 amount_too_low | A live Pix charge needs at least 500 (R$ 5,00). |
422 invalid_request | A field is missing or wrong. message names the first problem and problems lists all of them. |
500 server_error | Something failed on our side. Try again; if it keeps happening, contact support. |
{
"error": {
"code": "invalid_request",
"message": "value is required: an integer in cents, minimum 50 (R$ 0,50)",
"problems": [
"value is required: an integer in cents, minimum 50 (R$ 0,50)"
]
}
}Integrations built on the earlier POST /v1/checkout/sessions endpoint keep working. New integrations should use /v1/charges.