PovitoDevelopers

SDKs and plugins#

Every guide on this site shows plain HTTP — curl and Node's built-in fetch — because the API is small enough to call directly. The SDKs remove the boilerplate: idempotency keys, retries on 429 and 5xx, typed responses and webhook verification.

Package Platform Status
@povito/checkout-node Node.js 18+ Phase 1 — in this repository, publishing with the first release
@povito/checkout-js Browser drop-in Phase 2 — see embedded checkout
povito_checkout Flutter / Dart Phase 2
povito-checkout-php and a WooCommerce plugin PHP Phase 3, gated on licensing

@povito/checkout-node#

No dependencies; uses the runtime's fetch.

Installbash
npm install @povito/checkout-node

Create a session and send the shopper to it#

js
import { PovitoCheckout } from "@povito/checkout-node"

const povito = new PovitoCheckout({ secretKey: process.env.POVITO_CHECKOUT_SECRET_KEY })

const session = await povito.sessions.create({
  reference_id: "POV-1041",
  amount: 45000,            // integer minor units — IQD has none, so this is 45,000 dinars
  currency: "IQD",
  line_items: [
    { label: "Basket", amount: 42000, type: "charge" },
    { label: "Delivery — Hi-Express", amount: 5000, type: "shipping" },
    { label: "Coupon RAMADAN", amount: 2000, type: "discount" },
  ],
  payment_method_types: ["zaincash", "fib", "card"],
  customer: { phone: "+9647800000000", name: "Ali M. Ismail" },
  locale: "ar",
  success_url: "https://your-store.example/checkout/return",
  cancel_url: "https://your-store.example/cart",
  metadata: { order_id: "POV-1041" },
})

// redirect the shopper
res.redirect(session.url)

An Idempotency-Key is generated for every creating call; pass your own with { idempotencyKey } as the second argument to make retries across processes safe.

Verify by reference, never by the return URL#

js
const session = await povito.sessions.retrieve(id)
if (session.payment_status === "paid") fulfil(session.reference_id)

Also available: sessions.list({ reference_id, status, created_after, limit, cursor }), sessions.expire(id) and paymentMethods.list().

Webhooks#

js
import { constructEvent, WebhookSignatureError } from "@povito/checkout-node"

app.post("/webhooks/povito", express.raw({ type: "application/json" }), async (req, res) => {
  let event
  try {
    event = constructEvent(req.body, req.header("povito-signature"), process.env.POVITO_WEBHOOK_SECRET)
  } catch (e) {
    if (e instanceof WebhookSignatureError) return res.status(401).end()
    throw e
  }
  if (event.type === "checkout.session.completed") {
    // treat the webhook as a prompt: re-fetch, then act
    const session = await povito.sessions.retrieve(event.data.object.id)
    if (session.payment_status === "paid") await fulfil(session.reference_id)
  }
  res.status(200).end()
})

constructEvent(rawBody, header, secret, toleranceSeconds = 300) verifies the raw bytes with a constant-time compare and parses the JSON; verifySignature does only the first half, and signPayload(rawBody, secret) produces a header the way Povito does so you can test your handler. Handle events idempotently by event.id; Povito retries on non-2xx at 1 m, 5 m, 30 m, 2 h, 6 h, 24 h, 24 h.

webhookEndpoints.create / list / retrieve / update / del and events.list / retrieve / redeliver cover the rest of the webhooks API.

Refunds#

js
await povito.refunds.create({ session_id: session.id, amount: 5000, reason: "Customer returned one item, ticket #4521" })

Plus refunds.retrieve(id) and refunds.list({ session_id }).

Errors#

Every non-2xx response throws PovitoCheckoutError with status, code (for example amount_mismatch, idempotency_key_conflict, refund_exceeds_captured), details[] and requestId. GETs and keyed POSTs are retried on 429 and 5xx with backoff — safe because of the idempotency key.

Test mode#

Use a povito_ck_test_… key. Sessions created with it render the simulated test gateway; the test phone +9647500000000 accepts OTP 000000. See testing.