VKX Pay
You price in fiat; the payer picks the crypto. The merchant amount and the VKX fee move in the same on-chain transaction — either both happen or neither does. No custody at any point.
البداية
Before any code: create the account at /pay/painel with your Google account and register one wallet address per network. That is the only setup there is.
npm install @vkx/pay
إنشاء الفاتورة
On your server — the secret key never reaches the browser.
const { createPaymentIntent } = require('@vkx/pay');
const payment = await createPaymentIntent({
apiKey: process.env.VKX_PAY_SECRET_KEY, // sk_vkxpay_...
amount: '199.90', // reais, string
orderId: pedido.id, // o id no SEU sistema
});
// payment.checkoutUrl → mande o pagador para cá
// (botão no site, link no WhatsApp, QR no balcão)
المعاملات
| الحقل | مطلوب | الوصف |
|---|---|---|
apiKey | نعم | Your secret key sk_vkxpay_... |
amount | نعم | Amount in fiat, as a string: '199.90' |
orderId | لا | Order id in your system — comes back in the webhook |
metadata | لا | Free-form object, stored with the charge |
expiresInSeconds | لا | Charge validity (default 1800, between 120 and 86400) |
استلام التأكيد
When the blockchain confirms, VKX calls your URL. Use the raw request body — before any JSON.parse from your framework.
const { constructWebhookEvent } = require('@vkx/pay');
app.post('/webhooks/vkx', express.raw({ type: 'application/json' }), (req, res) => {
const event = constructWebhookEvent({
secret: process.env.VKX_PAY_WEBHOOK_SECRET, // whsec_...
body: req.body,
signature: req.headers['x-vkx-signature'],
});
if (event.event === 'payment.completed') {
liberarPedido(event.data.orderId);
}
res.sendStatus(200);
});
The signature arrives as t=<unix>,v1=<hmac>, an HMAC-SHA256 of "<t>.<body>". The SDK rejects tampered bodies, wrong secrets and stale requests (older than 5 minutes).
Event body
{
"event": "payment.completed",
"createdAt": "2026-08-15T18:30:44.603Z",
"data": {
"paymentIntentId": "pay_c-M_7otXmizP",
"orderId": "PEDIDO-123",
"amount": "199.90",
"currency": "BRL",
"network": "bsc",
"txHash": "0x…",
"payer": "0x…",
"confirmedAt": "2026-08-15T18:31:02.114Z"
}
}
Events: payment.completed and payment.expired. Reply 2xx; VKX retries with increasing backoff (1min, 5min, 30min, 2h, 6h, 12h, 24h) before giving up.
حالات الفاتورة
| الحالة | المعنى |
|---|---|
OPEN | Created, waiting for payment |
PENDING | Transaction seen on-chain, awaiting confirmations |
PAID | Confirmed — the webhook was fired |
EXPIRED | Expired with no payment |
CANCELED | Cancelled by you before payment |
REFUNDED_EXTERNAL | Refunded outside and recorded in the history |
الشبكات والعملات
| الشبكة | العملات | التأكيدات |
|---|---|---|
| BNB Chain | USDT, USDC, USD1, BNB | 3 |
| Polygon | USDT, USDC, POL | 30 |
| Base | USDC, ETH | 12 |
| Solana | USDC, SOL | finalized |
Native coins are optional — you enable them in the dashboard. The fiat quote is frozen during checkout (about 5 minutes for stablecoins, less for native coins, which move more).
واجهات REST
If you do not use Node.js, talk straight to the API. Base: https://api.vkxtech.com.br
| الطريقة | المسار | الوظيفة |
|---|---|---|
POST | /pay/v1/payment-intents | Creates the charge |
GET | /pay/v1/payment-intents/:id | Reads status and payments |
POST | /pay/v1/payment-intents/:id/cancel | Cancels before payment |
POST | /pay/v1/payment-intents/:id/refunded-external | Records a refund made outside |
PUT | /pay/v1/wallets | Sets where to receive on each network |
POST | /pay/v1/webhook-endpoints | Registers the webhook URL |
GET | /pay/v1/me | Sales summary (accepts the read key) |
curl -X POST https://api.vkxtech.com.br/pay/v1/payment-intents \
-H "Authorization: Bearer sk_vkxpay_..." \
-H "Content-Type: application/json" \
-d '{"amount":"199.90","orderId":"PEDIDO-123"}'
لغات أخرى
The npm package is convenience, not a requirement. VKX Pay is a REST API with JSON: any language that makes an HTTP request integrates — PHP, Python, Ruby, Go, Java, C#, Elixir, whatever you use.
There are only two operations: create the charge (one authenticated request) and validate the webhook (an HMAC-SHA256, which every language has in its standard library).
Python
import requests, hmac, hashlib, time
# criar a cobrança
r = requests.post(
"https://api.vkxtech.com.br/pay/v1/payment-intents",
headers={"Authorization": f"Bearer {VKX_PAY_SECRET_KEY}"},
json={"amount": "199.90", "orderId": pedido_id},
).json()
checkout_url = r["checkoutUrl"]
# validar o webhook
def valido(corpo_cru: bytes, header: str, segredo: str) -> bool:
partes = dict(p.split("=", 1) for p in header.split(","))
t = int(partes["t"])
if abs(time.time() - t) > 300:
return False
esperado = hmac.new(segredo.encode(),
f"{t}.".encode() + corpo_cru,
hashlib.sha256).hexdigest()
return hmac.compare_digest(esperado, partes["v1"])
Go
body, _ := json.Marshal(map[string]string{"amount": "199.90", "orderId": pedidoID})
req, _ := http.NewRequest("POST", apiBase+"/pay/v1/payment-intents", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("VKX_PAY_SECRET_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
Anything that runs cURL
curl -X POST https://api.vkxtech.com.br/pay/v1/payment-intents \
-H "Authorization: Bearer $VKX_PAY_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"amount":"199.90","orderId":"PEDIDO-123"}'
WordPress
WordPress is PHP, so the integration uses wp_remote_post — no plugin and no external dependency. Paste into your theme's functions.php (or your own plugin):
<?php
function vkx_criar_cobranca($valor, $pedido_id) {
$resposta = wp_remote_post('https://api.vkxtech.com.br/pay/v1/payment-intents', [
'headers' => [
'Authorization' => 'Bearer ' . VKX_PAY_SECRET_KEY,
'Content-Type' => 'application/json',
],
'body' => wp_json_encode([
'amount' => $valor, // '199.90'
'orderId' => $pedido_id,
]),
'timeout' => 20,
]);
if (is_wp_error($resposta)) return null;
$dados = json_decode(wp_remote_retrieve_body($resposta), true);
return $dados['checkoutUrl'] ?? null; // redirecione o comprador para cá
}
// Webhook: registre uma rota REST e valide a assinatura
add_action('rest_api_init', function () {
register_rest_route('vkx/v1', '/webhook', [
'methods' => 'POST',
'permission_callback' => '__return_true',
'callback' => function (WP_REST_Request $req) {
$corpo = $req->get_body(); // corpo CRU
$header = $req->get_header('x-vkx-signature');
parse_str(str_replace(',', '&', $header), $p); // t=...,v1=...
if (abs(time() - (int) $p['t']) > 300) return new WP_REST_Response('velho', 400);
$esperado = hash_hmac('sha256', $p['t'] . '.' . $corpo, VKX_PAY_WEBHOOK_SECRET);
if (!hash_equals($esperado, $p['v1'])) return new WP_REST_Response('invalido', 400);
$evento = json_decode($corpo, true);
if ($evento['event'] === 'payment.completed') {
// marque o pedido como pago: $evento['data']['orderId']
}
return new WP_REST_Response('ok', 200);
},
]);
});
In WooCommerce, call vkx_criar_cobranca() in your gateway and redirect to checkoutUrl; in the webhook, use $order->payment_complete().
React / Next.js
The secret key never reaches the browser. Create the charge on the server and return only the checkout URL.
// app/api/pagar/route.ts (Next.js App Router)
import { createPaymentIntent } from '@vkx/pay';
export async function POST(req: Request) {
const { orderId, total } = await req.json();
const payment = await createPaymentIntent({
apiKey: process.env.VKX_PAY_SECRET_KEY!,
amount: total,
orderId,
});
return Response.json({ checkoutUrl: payment.checkoutUrl });
}
// client component
async function pagar() {
const r = await fetch('/api/pagar', {
method: 'POST',
body: JSON.stringify({ orderId: pedido.id, total: '199.90' }),
}).then((r) => r.json());
window.location.assign(r.checkoutUrl);
}
مفاتيح الواجهة
| المفتاح | الصلاحية | أين يُستخدم |
|---|---|---|
sk_vkxpay_… | Create charges, configure the account | Server only |
rk_vkxpay_… | Read-only: query sales | Dashboards and apps |
whsec_… | Validate the webhook signature | Server only |
Each key is shown once when created — we store only a hash. Lost it? Generate another and revoke the previous one in the dashboard.
اللوحة على الهاتف
Generate an rk_ key in the dashboard and use it in the VKX Wallet app to follow sales from your phone. Being read-only, it cannot create a charge or move funds, even if the phone is lost.
curl https://api.vkxtech.com.br/pay/v1/me \
-H "Authorization: Bearer rk_vkxpay_..."
The response carries volume today, total volume, number of sales, average ticket, movement per network and the latest charges.