Developers

An API you can read end to end.

Contract-first OpenAPI, idempotent writes, signed webhooks and custodial processing through Bitcart. Everything below uses placeholder keys — live keys are created in the dashboard after approval and shown once.

Quickstart

Create your first payment intent.

Each example creates the same payment intent and redirects to the hosted checkout. They work once live payments are enabled for your organisation.

curl
curl -X POST https://api.feitpay.com/v1/payment-intents \
  -H "Authorization: Bearer gp_live_YOUR_KEY" \
  -H "Idempotency-Key: order-123" \
  -H "Content-Type: application/json" \
  -d '{
    "store_id": "store_YOUR_STORE_ID",
    "amount": "59.90",
    "currency": "EUR",
    "allowed_methods": ["crypto"],
    "merchant_order_id": "order-123",
    "return_url": "https://merchant.example/thank-you"
  }'
TypeScript · @gryphem-pay/sdk
import { Gryphem } from '@gryphem-pay/sdk';

const gp = new Gryphem({ apiKey: process.env.GRYPHEM_API_KEY!, baseUrl: 'https://api.feitpay.com' }); // gp_live_…

const intent = await gp.paymentIntents.create(
  {
    store_id: 'store_YOUR_STORE_ID',
    amount: '59.90',
    currency: 'EUR',
    merchant_order_id: 'order-123',
  },
  { idempotencyKey: 'order-123' },
);

redirect(intent.checkout_url);
Go · gryphem-pay-go
client, err := gryphempay.New(os.Getenv("GRYPHEM_API_KEY"), gryphempay.WithBaseURL("https://api.feitpay.com"))
if err != nil {
    return err
}
intent, err := client.PaymentIntents.Create(ctx, &gryphempay.PaymentIntentCreateParams{
    StoreID:         "store_YOUR_STORE_ID",
    Amount:          "59.90",
    Currency:        "EUR",
    MerchantOrderID: "order-123",
    IdempotencyKey:  "order-123",
})
if err != nil {
    return err
}
http.Redirect(w, r, *intent.CheckoutURL, http.StatusSeeOther)
Python · gryphem-pay-python
from gryphem_pay import Gryphem

gp = Gryphem(api_key=os.environ["GRYPHEM_API_KEY"], base_url="https://api.feitpay.com")  # gp_live_…

intent = gp.payment_intents.create(
    store_id="store_YOUR_STORE_ID",
    amount="59.90",
    currency="EUR",
    merchant_order_id="order-123",
    idempotency_key="order-123",
)
return redirect(intent["checkout_url"])

SDK availability and versions are listed in the SDK documentation. The Go module path is a placeholder until the SDK is published.

Webhooks

Verify, deduplicate, then act.

Signatures are HMAC-SHA256 over the timestamp and the raw body. Event IDs are stable across retries.

Express · @gryphem-pay/webhooks
import { verifyWebhook, WebhookVerificationError } from '@gryphem-pay/webhooks';

app.post('/webhooks/gryphem', express.raw({ type: 'application/json' }), async (req, res) => {
  try {
    const event = await verifyWebhook({
      payload: req.body, // raw Buffer — never re-serialised JSON
      header: req.get('Gryphem-Signature') ?? '',
      secrets: [process.env.GRYPHEM_WEBHOOK_SECRET!], // add the previous secret during rotation
    });
    if (await seen(event.event_id)) return res.sendStatus(200); // idempotent
    if (event.event_type === 'payment.confirmed') await markOrderPaid(event.object.id);
    res.sendStatus(200);
  } catch (err) {
    if (err instanceof WebhookVerificationError) return res.sendStatus(400);
    throw err;
  }
});

Conventions

Predictable by design.

Idempotency
Send an Idempotency-Key on every create. Replays return the original response; a reused key with a different body is rejected.
Typed errors
Errors carry type, code, message, param and request_id. Retry only rate_limit_error and api_error, with backoff.
Cursor pagination
Lists accept limit and starting_after and return has_more and next_cursor — stable under concurrent writes.
Money as strings
Amounts are decimal strings; crypto amounts also come in integer base units. Never parse them into floats.
Request IDs
Every response has a Request-Id header. Quote it in support requests; it links to the request log in the dashboard.
Versioning
The API is versioned under /v1. Webhook payloads carry api_version so receivers can migrate deliberately.

Going live

Ready before the first payment.

Create your account and submit business verification now. Before your first live payment, make sure your integration handles every state: underpaid, overpaid, expired and manual review reach a person, and you fulfil only when a payment is fulfillable.