Merchant Configuration

Control x402 payment support for your HivePay merchant account.


Overview

x402 payments are enabled by default for all HivePay merchants. When enabled, x402-aware clients (AI agents, automated tools) can pay for your payment sessions directly through the checkout URL. When disabled, only browser-based checkout is available.


Toggle x402 Support

Via API

import { HivePay } from '@hivepay/client';

const hivepay = new HivePay({ apiKey: 'sk_live_xxx' });

// Disable x402 payments
await hivepay.merchants.update('your_merchant_id', {
  x402Enabled: false
});

// Re-enable x402 payments
await hivepay.merchants.update('your_merchant_id', {
  x402Enabled: true
});
use HivePay\HivePay;

$hivepay = new HivePay(['apiKey' => 'sk_live_xxx']);

// Disable x402 payments
$hivepay->merchants->update('your_merchant_id', [
    'x402Enabled' => false,
]);

// Re-enable x402 payments
$hivepay->merchants->update('your_merchant_id', [
    'x402Enabled' => true,
]);
# Disable x402 payments
curl -X POST https://hivepay.me/api/public/merchants/your_merchant_id \
  -H "x-api-key: sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "x402Enabled": false }'

# Re-enable x402 payments
curl -X POST https://hivepay.me/api/public/merchants/your_merchant_id \
  -H "x-api-key: sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "x402Enabled": true }'

Via Dashboard

Toggle the x402 Payments switch in your Dashboard Settings.


What Happens When Disabled

When x402 is disabled for your account:

  • x402 clients receive a 403 Forbidden error when visiting your checkout URLs
  • Browser users still see the normal checkout page and can pay as usual
  • Existing completed sessions are not affected
  • You can re-enable x402 at any time

Fee Structure

x402 payments use the same fee structure as standard HivePay checkout payments. The percentage-based fee is included in the 402 response and enforced at settlement time.

The 402 response includes a fee breakdown in the extra field:

{
  "extra": {
    "sessionId": "cmj7b2rg10004d2rimvum8kaz",
    "feeAccount": "hivepay",
    "feeAmount": "0.158 HBD",
    "netAmount": "10.342 HBD"
  }
}

The x402 client must sign a transaction with two transfer operations:

Transfer Recipient Amount
Net payment Merchant's Hive account netAmount
Fee HivePay's fee account (feeAccount) feeAmount

Both transfers must be from the same sender and in the same currency as the payment session.


Security

HivePay performs several checks on every x402 payment:

  1. Session validation — the payment session must exist, be in pending status, and not expired
  2. Merchant check — the merchant must have x402 enabled
  3. Transaction structure — exactly two transfer operations (net + fee)
  4. Recipient validation — net transfer goes to the merchant, fee transfer goes to HivePay
  5. Amount validation — both transfers meet or exceed the required amounts
  6. Currency validation — transfers match the session currency (HIVE or HBD)
  7. Nonce uniqueness — each payment nonce can only be used once (prevents replay attacks)
  8. Signature verification — the transaction signature is verified against the sender's on-chain active public keys
  9. Expiration check — the transaction must not be expired

API Endpoints

HivePay also exposes session-aware x402 endpoints for programmatic use:

Endpoint Method Description
/api/public/x402/health GET Health check
/api/public/x402/supported-networks GET Returns ["hive:mainnet"]
/api/public/x402/verify POST Verify a payment payload for a session
/api/public/x402/settle POST Verify, broadcast, and complete a session

Using the SDK

The @hivepay/client (TypeScript) and hivepay/client (PHP) SDKs provide built-in methods for the verify and settle endpoints:

import { HivePay } from '@hivepay/client';

const hivepay = new HivePay({ apiKey: 'sk_live_xxx' });

// Verify without broadcasting
const verify = await hivepay.payments.x402Verify('session_id', {
  x402Version: 1,
  scheme: 'exact',
  network: 'hive:mainnet',
  payload: { signedTransaction: tx, nonce: 'unique-nonce' }
});

if (verify.isValid) {
  // Settle — broadcasts and completes the session
  const settle = await hivepay.payments.x402Settle('session_id', {
    x402Version: 1,
    scheme: 'exact',
    network: 'hive:mainnet',
    payload: { signedTransaction: tx, nonce: 'unique-nonce' }
  });

  console.log(settle.txId, settle.payer);
}
use HivePay\HivePay;

$hivepay = new HivePay(['apiKey' => 'sk_live_xxx']);

$payload = [
    'x402Version' => 1,
    'scheme' => 'exact',
    'network' => 'hive:mainnet',
    'payload' => ['signedTransaction' => $tx, 'nonce' => 'unique-nonce'],
];

// Verify without broadcasting
$verify = $hivepay->payments->x402Verify('session_id', $payload);

if ($verify['isValid']) {
    // Settle — broadcasts and completes the session
    $settle = $hivepay->payments->x402Settle('session_id', $payload);
    echo $settle['txId'] . ' ' . $settle['payer'];
}
# Verify
curl -X POST https://hivepay.me/api/public/x402/verify \
  -H "Content-Type: application/json" \
  -d '{
    "sessionId": "cmj7b2rg10004d2rimvum8kaz",
    "paymentPayload": {
      "x402Version": 1,
      "scheme": "exact",
      "network": "hive:mainnet",
      "payload": {
        "signedTransaction": { ... },
        "nonce": "a1b2c3d4e5f6..."
      }
    }
  }'

# Settle
curl -X POST https://hivepay.me/api/public/x402/settle \
  -H "Content-Type: application/json" \
  -d '{
    "sessionId": "cmj7b2rg10004d2rimvum8kaz",
    "paymentPayload": {
      "x402Version": 1,
      "scheme": "exact",
      "network": "hive:mainnet",
      "payload": {
        "signedTransaction": { ... },
        "nonce": "a1b2c3d4e5f6..."
      }
    }
  }'

Settle Response

{
  "success": true,
  "txId": "abc123def456...",
  "payer": "alice"
}

Next Steps