Payments API

List invoices, create checkout sessions, poll payment status, and discover the payment methods and cryptocurrencies available to an account. These endpoints are browser/checkout-oriented: creating a payment returns an identifier you poll (and, for card and SBP gates, a hosted checkout URL to redirect the customer to). All responses are flat, bespoke JSON — they do NOT use the standard {success, payload, meta, errors, description} resource envelope.

A payment moves through statuses (createdcompleted, or expires). Card and SBP gates redirect the customer to a hosted checkout page; crypto gates return an on-chain address the customer sends funds to. Use Check Payment to poll for completion — it doubles as a webhook fallback and returns the updated traffic balance once the payment lands.

List Payments

GET /v1/payments Requires Auth

List Payments

List Payments

Retrieve the authenticated user's invoice history, newest first. Each row includes the invoice identifier, a formatted total price string, the current status, and a link to download the invoice document.

No input needed — runs live with your key
curl https://api.proxyhat.com/v1/payments \
  -H "Authorization: Bearer __API_KEY__" \
  -H "Accept: application/json"
import requests

response = requests.get(
    "https://api.proxyhat.com/v1/payments",
    headers={
        "Authorization": "Bearer __API_KEY__",
        "Accept": "application/json",
    },
)

result = response.json()
for p in result["data"]:
    print(p["invoice_id"], p["total_price"], p["status"])
const response = await fetch("https://api.proxyhat.com/v1/payments", {
  headers: {
    "Authorization": "Bearer __API_KEY__",
    "Accept": "application/json",
  },
});

const result = await response.json();
result.data.forEach(p => console.log(p.invoice_id, p.total_price, p.status));
req, _ := http.NewRequest("GET", "https://api.proxyhat.com/v1/payments", nil)
req.Header.Set("Authorization", "Bearer __API_KEY__")
req.Header.Set("Accept", "application/json")

resp, err := http.DefaultClient.Do(req)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

var result struct {
    Success bool                     `json:"success"`
    Data    []map[string]interface{} `json:"data"`
}
json.NewDecoder(resp.Body).Decode(&result)
for _, p := range result.Data {
    fmt.Println(p["invoice_id"], p["total_price"], p["status"])
}

payment_type is "Recurring" for subscription plans and "One-time" otherwise. purchase_category is "Subscription" or "Pay-As-You-Go" correspondingly. total_price is a pre-formatted string (e.g. "$50.00"), not a number, and amount_traffic is the plan's GB snapshot (may be null). download_url points at the invoice document endpoint. On an internal error the endpoint returns 500 with {"success": false, "message": ...}.

Create Payment

POST /v1/payments Requires Auth

Create Payment

Create Payment

Create a checkout session for a plan. Returns a payment_id you can poll, plus a checkout_url for the card and SBP gates. Subscriptions are card-only. ISP plans require ISP proxy access on the account.

Example request: type plan_id gate cryptocurrency_code
Request Body
Name Type Required Description
type string Required Plan family. One of "regular" (pay-as-you-go), "subscription", or "isp".
plan_id string Required The id of the plan to purchase (matches the type: RegularPlan, SubscriptionPlan, or IspPlan).
gate string Required Payment method. One of "card", "crypto", "crypto_setype", or "sbp". Subscriptions require "card". Only gates enabled in config are accepted (see Available Methods).
cryptocurrency_code string Optional Required when gate = "crypto". For crypto gates the code must be supported for that gate (see List Cryptocurrencies). Ignored for card/SBP.
coupon_code string Optional Optional discount/bonus coupon code applied to the order.
quantity integer Optional ISP only. Number of proxies to buy, 1-10 (also server-capped by the configured max buy quantity). Defaults to 1; forced to 1 for non-ISP types.
auto_renew boolean Optional ISP only, and only with gate = "card" (auto-charge). Enables automatic renewal of the ISP proxy.
attribution object Optional Optional Google Ads click identifiers, persisted for offline-conversion upload.
attribution.gclid string Optional Google Ads click id (max 512 chars).
attribution.wbraid string Optional Google Ads web-to-app click id (max 512 chars).
attribution.gbraid string Optional Google Ads app-to-web click id (max 512 chars).
curl -X POST https://api.proxyhat.com/v1/payments \
  -H "Authorization: Bearer __API_KEY__" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "type": "regular",
    "plan_id": "b7e2...plan-id",
    "gate": "card"
  }'
import requests

response = requests.post(
    "https://api.proxyhat.com/v1/payments",
    headers={
        "Authorization": "Bearer __API_KEY__",
        "Accept": "application/json",
    },
    json={
        "type": "regular",
        "plan_id": "b7e2...plan-id",
        "gate": "card",
    },
)

result = response.json()
print(result["payment_id"], result.get("checkout_url"))
const response = await fetch("https://api.proxyhat.com/v1/payments", {
  method: "POST",
  headers: {
    "Authorization": "Bearer __API_KEY__",
    "Content-Type": "application/json",
    "Accept": "application/json",
  },
  body: JSON.stringify({
    type: "regular",
    plan_id: "b7e2...plan-id",
    gate: "card",
  }),
});

const result = await response.json();
console.log(result.payment_id, result.checkout_url);
payload := map[string]interface{}{
    "type":    "regular",
    "plan_id": "b7e2...plan-id",
    "gate":    "card",
}
body, _ := json.Marshal(payload)

req, _ := http.NewRequest("POST", "https://api.proxyhat.com/v1/payments", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer __API_KEY__")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")

resp, err := http.DefaultClient.Do(req)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
fmt.Println(result["payment_id"], result["checkout_url"])

checkout_url is present only for the card and sbp gates — redirect the customer there to complete payment. Crypto gates omit it; instead call Get Payment to read the pay address / hosted checkout. A scheduled subscription downgrade does not charge and returns a different success body: {"success": true, "downgrade_scheduled": true, "applies_at": "..."}.

Rate limits & errors. An account may hold at most 5 pending payments and create at most 3 per hour. Errors return success: false with a message (and often an extra field):

  • 422 too many pending payments — message, pending_payment_id, pending_count.
  • 429 hourly creation limit reached — message, pending_payment_id.
  • 422 code: "SUBSCRIPTION_REQUIRES_CARD" — a subscription was attempted with a non-card gate.
  • 422 code: "NO_RECURRING_SUBSCRIPTION" / "PLAN_NOT_SYNCED" — downgrade could not be scheduled.
  • 500 code: "DOWNGRADE_SCHEDULE_FAILED" — the downgrade schedule call failed.
  • 422 auto-renew requested with a non-card gate, plan not found / under construction, payment-limit reached, coupon invalid, or gate unavailable.
  • 403 ISP plan requested without ISP proxy access on the account.

Available Methods

GET /v1/payments/geo Requires Auth

Available Payment Methods

Available Payment Methods

Return the payment methods available to the caller, based on detected country (CloudFlare geo header, falling back to IP lookup) and which gateways are configured. Use this to decide which gate values are valid before creating a payment.

No input needed — runs live with your key
curl https://api.proxyhat.com/v1/payments/geo \
  -H "Authorization: Bearer __API_KEY__" \
  -H "Accept: application/json"
import requests

response = requests.get(
    "https://api.proxyhat.com/v1/payments/geo",
    headers={
        "Authorization": "Bearer __API_KEY__",
        "Accept": "application/json",
    },
)

result = response.json()
print(result["country_code"], result["methods"])
const response = await fetch("https://api.proxyhat.com/v1/payments/geo", {
  headers: {
    "Authorization": "Bearer __API_KEY__",
    "Accept": "application/json",
  },
});

const result = await response.json();
console.log(result.country_code, result.methods);
req, _ := http.NewRequest("GET", "https://api.proxyhat.com/v1/payments/geo", nil)
req.Header.Set("Authorization", "Bearer __API_KEY__")
req.Header.Set("Accept", "application/json")

resp, err := http.DefaultClient.Do(req)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

var result struct {
    Success     bool     `json:"success"`
    CountryCode string   `json:"country_code"`
    Methods     []string `json:"methods"`
}
json.NewDecoder(resp.Body).Decode(&result)
fmt.Println(result.CountryCode, result.Methods)

methods always includes "card"; "crypto", "crypto_setype", and "sbp" appear only when their respective gateways are configured. country_code may be an empty string if geo detection fails.

List Cryptocurrencies

GET /v1/payments/cryptocurrencies Requires Auth

List Cryptocurrencies

List Cryptocurrencies

List the cryptocurrencies accepted for payment. Pass a gate to filter to the coins supported by that specific crypto gateway. Use a returned code as cryptocurrency_code when creating a crypto payment.

No input needed — runs live with your key
Query Parameters
Name Type Required Description
gate string Optional Restrict the list to coins supported by a gate (e.g. "crypto" or "crypto_setype"). Omit to return all.
curl "https://api.proxyhat.com/v1/payments/cryptocurrencies?gate=crypto" \
  -H "Authorization: Bearer __API_KEY__" \
  -H "Accept: application/json"
import requests

response = requests.get(
    "https://api.proxyhat.com/v1/payments/cryptocurrencies",
    headers={
        "Authorization": "Bearer __API_KEY__",
        "Accept": "application/json",
    },
    params={"gate": "crypto"},
)

result = response.json()
for coin in result["data"]:
    print(coin["code"], coin["label"], coin["network"])
const params = new URLSearchParams({ gate: "crypto" });

const response = await fetch(`https://api.proxyhat.com/v1/payments/cryptocurrencies?${params}`, {
  headers: {
    "Authorization": "Bearer __API_KEY__",
    "Accept": "application/json",
  },
});

const result = await response.json();
result.data.forEach(c => console.log(c.code, c.label, c.network));
req, _ := http.NewRequest("GET", "https://api.proxyhat.com/v1/payments/cryptocurrencies?gate=crypto", nil)
req.Header.Set("Authorization", "Bearer __API_KEY__")
req.Header.Set("Accept", "application/json")

resp, err := http.DefaultClient.Do(req)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

var result struct {
    Success bool                     `json:"success"`
    Data    []map[string]interface{} `json:"data"`
}
json.NewDecoder(resp.Body).Decode(&result)
for _, c := range result.Data {
    fmt.Println(c["code"], c["label"], c["network"])
}

Get Payment

GET /v1/payments/{payment} Requires Auth

Get Payment

Get Payment

Retrieve the payment details needed to complete checkout. The data object is gate-dependent: crypto gates return the on-chain pay address and coin info; card and SBP gates return a hosted checkout_url. Only the owner may fetch a payment.

We filled these in for you: payment

Tweak any value if you like — or just press Try.

Path Parameters
Name Type Required Description
payment string (uuid) Required The payment id (uuid) returned by Create Payment.
curl https://api.proxyhat.com/v1/payments/9b1f7c2e-4d3a-4a1b-9e2c-1f0a2b3c4d5e \
  -H "Authorization: Bearer __API_KEY__" \
  -H "Accept: application/json"
import requests

payment_id = "9b1f7c2e-4d3a-4a1b-9e2c-1f0a2b3c4d5e"

response = requests.get(
    f"https://api.proxyhat.com/v1/payments/{payment_id}",
    headers={
        "Authorization": "Bearer __API_KEY__",
        "Accept": "application/json",
    },
)

data = response.json()["data"]
print(data["gate"], data["status"])
const paymentId = "9b1f7c2e-4d3a-4a1b-9e2c-1f0a2b3c4d5e";

const response = await fetch(`https://api.proxyhat.com/v1/payments/${paymentId}`, {
  headers: {
    "Authorization": "Bearer __API_KEY__",
    "Accept": "application/json",
  },
});

const { data } = await response.json();
console.log(data.gate, data.status);
paymentId := "9b1f7c2e-4d3a-4a1b-9e2c-1f0a2b3c4d5e"
url := fmt.Sprintf("https://api.proxyhat.com/v1/payments/%s", paymentId)

req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Authorization", "Bearer __API_KEY__")
req.Header.Set("Accept", "application/json")

resp, err := http.DefaultClient.Do(req)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

var result struct {
    Success bool                   `json:"success"`
    Data    map[string]interface{} `json:"data"`
}
json.NewDecoder(resp.Body).Decode(&result)
fmt.Println(result.Data["gate"], result.Data["status"])

The data shape depends on gate. product_type is one of "regular", "subscription", or "isp". All gates return gate, product_type, amount_usd, status, expires_at, completed_at, and is_first_paid. In addition:

  • cardcheckout_url (redirect the customer here).
  • sbpamount_rub and checkout_url.
  • crypto / crypto_setypepay_address, crypto_amount, checkout_url (hosted-checkout URL, may be null), hosted_checkout (bool), crypto (coin descriptor), and tx_hash.

An invalid uuid or a payment not owned by the caller returns 404 with {"success": false, "message": ...}.

Check Payment

GET /v1/payments/{payment}/check Requires Auth

Check Payment Status

Check Payment Status

Poll a payment for completion. Acts as a webhook fallback — it queries the underlying gateway when the local status is not yet completed. Once completed, the response also carries the account's updated traffic balance. Only the owner may check a payment.

We filled these in for you: payment

Tweak any value if you like — or just press Try.

Path Parameters
Name Type Required Description
payment string (uuid) Required The payment id (uuid) to poll.
curl https://api.proxyhat.com/v1/payments/9b1f7c2e-4d3a-4a1b-9e2c-1f0a2b3c4d5e/check \
  -H "Authorization: Bearer __API_KEY__" \
  -H "Accept: application/json"
import requests

payment_id = "9b1f7c2e-4d3a-4a1b-9e2c-1f0a2b3c4d5e"

response = requests.get(
    f"https://api.proxyhat.com/v1/payments/{payment_id}/check",
    headers={
        "Authorization": "Bearer __API_KEY__",
        "Accept": "application/json",
    },
)

data = response.json()["data"]
print(data["status"])
if data["status"] == "completed":
    print(data["traffic"]["total_human"])
const paymentId = "9b1f7c2e-4d3a-4a1b-9e2c-1f0a2b3c4d5e";

const response = await fetch(`https://api.proxyhat.com/v1/payments/${paymentId}/check`, {
  headers: {
    "Authorization": "Bearer __API_KEY__",
    "Accept": "application/json",
  },
});

const { data } = await response.json();
console.log(data.status);
if (data.status === "completed") {
  console.log(data.traffic.total_human);
}
paymentId := "9b1f7c2e-4d3a-4a1b-9e2c-1f0a2b3c4d5e"
url := fmt.Sprintf("https://api.proxyhat.com/v1/payments/%s/check", paymentId)

req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Authorization", "Bearer __API_KEY__")
req.Header.Set("Accept", "application/json")

resp, err := http.DefaultClient.Do(req)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

var result struct {
    Success bool                   `json:"success"`
    Data    map[string]interface{} `json:"data"`
}
json.NewDecoder(resp.Body).Decode(&result)
fmt.Println(result.Data["status"])

While the payment is still pending, data contains only gate, product_type, status, and tx_hash. The is_first_paid flag and the traffic object are added only once status is "completed". A payment not owned by the caller returns 403.

Invoice Document

Not a JSON endpoint. GET /v1/payments/{payment}/invoice returns a rendered document, not JSON — an HTML invoice by default, or a streamed PDF when ?format= is set to anything other than html. This is the target of each list row's download_url and is intended for browser display / download. Only the payment owner may access it (403 otherwise).

GET /v1/payments/{payment}/invoice Requires Auth

Download Invoice

Download Invoice

Render the invoice for a payment. Returns HTML by default, or a streamed PDF when format is not "html". This endpoint returns a document, not JSON — open it in a browser or save the stream to a file.

We filled these in for you: payment

Tweak any value if you like — or just press Try.

Query Parameters
Name Type Required Description
format string Optional Output format. "html" (default) returns an HTML page; any other value (e.g. "pdf") streams a PDF document.
Path Parameters
Name Type Required Description
payment string (uuid) Required The payment id (uuid) to render an invoice for.
# HTML (default)
curl https://api.proxyhat.com/v1/payments/9b1f7c2e-4d3a-4a1b-9e2c-1f0a2b3c4d5e/invoice \
  -H "Authorization: Bearer __API_KEY__"

# PDF download
curl "https://api.proxyhat.com/v1/payments/9b1f7c2e-4d3a-4a1b-9e2c-1f0a2b3c4d5e/invoice?format=pdf" \
  -H "Authorization: Bearer __API_KEY__" \
  -o invoice.pdf
import requests

payment_id = "9b1f7c2e-4d3a-4a1b-9e2c-1f0a2b3c4d5e"

response = requests.get(
    f"https://api.proxyhat.com/v1/payments/{payment_id}/invoice",
    headers={"Authorization": "Bearer __API_KEY__"},
    params={"format": "pdf"},
)

with open("invoice.pdf", "wb") as f:
    f.write(response.content)
import { writeFile } from "node:fs/promises";

const paymentId = "9b1f7c2e-4d3a-4a1b-9e2c-1f0a2b3c4d5e";

const response = await fetch(
  `https://api.proxyhat.com/v1/payments/${paymentId}/invoice?format=pdf`,
  { headers: { "Authorization": "Bearer __API_KEY__" } },
);

const buffer = Buffer.from(await response.arrayBuffer());
await writeFile("invoice.pdf", buffer);
paymentId := "9b1f7c2e-4d3a-4a1b-9e2c-1f0a2b3c4d5e"
url := fmt.Sprintf("https://api.proxyhat.com/v1/payments/%s/invoice?format=pdf", paymentId)

req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Authorization", "Bearer __API_KEY__")

resp, err := http.DefaultClient.Do(req)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

out, _ := os.Create("invoice.pdf")
defer out.Close()
io.Copy(out, resp.Body)