Creating Payments

Learn how to create payment sessions and redirect customers to checkout.


Basic Payment

Create a simple payment with amount, currency, and description:

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

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

const payment = await hivepay.payments.create({
  amount: '10500',          // 10.500 (in satoshis/smallest unit)
  currency: 'HBD',          // 'HIVE' or 'HBD'
  description: 'Order #123' // Shown to customer
});

console.log(payment.id);          // Payment ID
console.log(payment.checkoutUrl); // Hosted checkout page URL
console.log(payment.status);      // 'pending'
curl -X POST https://hivepay.me/api/public/payments \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": "10500",
    "currency": "HBD",
    "description": "Order #123"
  }'

From a Product Price

Instead of an inline amount, charge a reusable catalog price by passing priceId (with an optional quantity), or lineItems for a multi-item cart. All line items must share the same currency.

// Single price
const payment = await hivepay.payments.create({ priceId: 'price_xxx', quantity: 2 });

// Multiple line items
const cart = await hivepay.payments.create({
  lineItems: [
    { priceId: 'price_plan' },
    { priceId: 'price_addon', quantity: 3 }
  ]
});
$payment = $hivepay->payments->create(['priceId' => 'price_xxx', 'quantity' => 2]);

The session snapshots the total amount, currency, and description, so it stays valid even if the product or price is later edited or archived.


Amount Format

Amounts are specified as strings in the smallest unit (satoshis):

Input Actual Amount
"1000" 1.000 HIVE/HBD
"10500" 10.500 HIVE/HBD
"100000" 100.000 HIVE/HBD

Both HIVE and HBD use 3 decimal precision.


Payment Options

Redirect URLs

Specify where to redirect customers after payment:

const payment = await hivepay.payments.create({
  amount: '10500',
  currency: 'HBD',
  description: 'Order #123',
  redirectUrl: 'https://yourstore.com/order/123/success',
  cancelUrl: 'https://yourstore.com/order/123/cancelled'
});
curl -X POST https://hivepay.me/api/public/payments \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": "10500",
    "currency": "HBD",
    "description": "Order #123",
    "redirectUrl": "https://yourstore.com/order/123/success",
    "cancelUrl": "https://yourstore.com/order/123/cancelled"
  }'

Pre-fill the payer's Hive account

If you already know which Hive account your customer will pay from, pass senderHiveAccount to pre-fill the payment form. The customer still reviews and confirms the account before signing — this step is never skipped. A leading @ is stripped and the account name is validated.

const payment = await hivepay.payments.create({
  amount: '10500',
  currency: 'HBD',
  description: 'Order #123',
  senderHiveAccount: 'alice'
});
curl -X POST https://hivepay.me/api/public/payments \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": "10500",
    "currency": "HBD",
    "description": "Order #123",
    "senderHiveAccount": "alice"
  }'

On the checkout page the customer sees who they are paying (recipientHiveAccount, the merchant's payout account) as fine print beneath the account field.

Custom Metadata

Attach custom data to a payment for your reference:

const payment = await hivepay.payments.create({
  amount: '10500',
  currency: 'HBD',
  description: 'Order #123',
  metadata: {
    orderId: '12345',
    customerId: 'cust_abc',
    items: ['item1', 'item2']
  }
});

// Metadata is returned with the payment
console.log(payment.metadata.orderId); // '12345'

Payment Response

A successful payment creation returns:

{
  "id": "cmj7b2rg10004d2rimvum8kaz",
  "slug": "order-12345-a1b2c3d4",
  "redirectUrl": "https://hivepay.me/for/order-12345-a1b2c3d4"
}
Field Description
id Unique payment identifier
slug URL-friendly identifier
redirectUrl URL to redirect customer to

Redirecting to Checkout

After creating a payment, redirect the customer to the checkout URL:

Payment Review
Payment Review
Payment Methods
Payment Methods
// Create payment on your server, then redirect
window.location.href = payment.checkoutUrl;
const handleCheckout = async () => {
  const payment = await createPayment(); // Your server call
  window.location.href = payment.checkoutUrl;
};

<button onClick={handleCheckout}>Pay with Hive</button>
<a href="https://hivepay.me/for/{paymentId}">Complete Payment</a>

After Payment

When the payment is completed or cancelled:

  1. Customer is redirected to your redirectUrl or cancelUrl
  2. A webhook is sent to your configured endpoint
  3. You can check the status using the payment ID

AML Limits

AML limits apply to the customer's Hive account, not to your merchant account — creating payments is never blocked by AML.

After the customer enters their Hive account and picks a payment method, HivePay checks the account's single payment and cumulative volume against the thresholds. If a limit is exceeded, the customer does not receive the transfer memo, so no funds are moved. They are asked to contact [email protected] to complete KYC verification.

For you, the payment ends in the failed status with failureReason: "aml_limit", and you receive the regular failed webhook. The payment's activity history shows an "AML Limit Reached" entry.


Error Handling

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

try {
  const payment = await hivepay.payments.create({
    amount: '10500',
    currency: 'HBD',
    description: 'Order #123'
  });
} catch (error) {
  if (isHivePayError(error)) {
    if (error.isForbidden()) {
      // Merchant account is inactive
      console.error('Forbidden:', error.message);
    } else if (error.isValidation()) {
      console.error('Invalid payment data:', error.message);
    } else if (error.isAuthError()) {
      console.error('Invalid API key');
    } else {
      console.error('Payment creation failed:', error.message);
    }
  }
}