orvacon
TypeScript-first · Payment orchestration

Provider-agnostic payment orchestration.

One clean API, any gateway. Iyzico, PayTR and bank virtual POS plug in as connectors behind a single type-safe interface — your code never knows which one is handling a payment.

v0.3 · Verified · Iyzico sandbox
payments.ts
import { iyzico } from "@orvacon/connector-iyzico";

const orva = orvacon({
  connectors: [iyzico({ apiKey, secretKey })],
});

// your code — identical for every gateway
const payment = await orva.authorize(order);
Pick a gateway

Swap the connector — your orva.authorize(…) call never changes.

Provider-agnosticTypeScript-firstNever holds moneyRuns in your runtimeMIT licensedEd25519-signed webhooksInteger minor unitsBring your own databaseDefault-deny RLSHash-chained ledgerTiming-safe cryptoIdempotent by constructionProvider-agnosticTypeScript-firstNever holds moneyRuns in your runtimeMIT licensedEd25519-signed webhooksInteger minor unitsBring your own databaseDefault-deny RLSHash-chained ledgerTiming-safe cryptoIdempotent by construction

One clean API. Any gateway. Connectors plug in behind a single type-safe interface so your application code never knows which gateway is handling a payment. Swap or add one by changing configuration, not logic.

01 — Overview

Your code shouldn't know the gateway.

You've written gateway-specific glue before — branching on provider, mapping error codes, re-implementing 3-D Secure for each one. orvacon moves that surface area behind a connector boundary.

One type-safe API in your app; gateway differences isolated to configuration. Swap or add a connector without touching application logic.

Your application
orchestrator
Iyzicoverified
PayTRroadmap
Bank POSroadmap
02 — The difference

Glue code, or a clean boundary.

The same payment, two ways. The hard parts of payments don't have to leak into your application code.

Before · gateway-specific glue
if (provider === "iyzico") {
  // sign, map fields, 3DS dance…
} else if (provider === "paytr") {
  // a totally different shape
}
total = amount * 1.18; // float drift
// verify the callback? trust the POST
verifyWebhook(req.body); // shared secret
With orvacon
// one shape, every gateway
const result = await orva.authorize(order);
Same call across every connector
Money stays integer minor units
3DS persisted; finalize signature verified
Idempotent by construction
03 — Why it's different

Built like infrastructure, not a wrapper.

Decisions made at the boundary — custody, types, money, signatures — so the hard parts of payments stay out of your code.

3.1

Never holds money

Funds flow from the cardholder straight to your own gateway account. No wallet, no balance, no payout — which keeps orvacon out of money-custody regulation and off your PCI-custody burden.

3.2

End-to-end type safety

Payment state and results are discriminated unions. The state machine is enforced at compile time — an invalid transition won't type-check.

type PaymentState =
  | "created" | "authorized"
  | "captured" | "refunded"
  | "requires_action"
  | "failed" | "voided";
3.3

Money as integer minor units

A branded Money type — integer minor units plus currency — keeps floats out of the core.

₺14.99{ 1499, "TRY" }
3.4

Bring your own database

No imposed datastore. Connect yours through an adapter — and the Supabase adapter generates a schema with default-deny row-level security out of the box.

3.5

Asymmetric webhook signatures

Outgoing webhooks are signed with Ed25519, so a leaked verification key can't forge events. Every signature and token check is timing-safe.

3.6

Idempotency & immutable ledger

Idempotency is enforced by a database unique constraint. The ledger is hash-chained — append-only and tamper-evident by construction.

04 — 3-D Secure

Stateful 3-D Secure, orchestrated.

3DS is two requests plus a browser wait. orvacon persists the in-between as requires_action, and trusts the signed finalize response — never the raw callback POST.

verification · iyzico sandboxPASS
01authorize()
authorize201
card detailsgateway
3-D Secure requested
In short

What orvacon is — and isn't.

One line you can hold onto, and three things it deliberately is not.

What it is

A stateful payment orchestrator that runs in your own runtime — coordinating gateways behind one type-safe API, and never touching the money.

What it isn't
Not an Iyzico wrapper. A stateful orchestrator across gateways, not a thin SDK around one.
Not an e-commerce platform. No catalog, cart, or inventory.
Not a wallet or marketplace. It never custodies funds.
Design principles

Built like infrastructure.

Three ideas that keep payments boring — the parts you'd want a payments core to get right before you trust it with a charge.

Cross-cutting core

Logging, errors, timeouts, retries and idempotency live in the core as interfaces — not bolted on as plugins.

Provider-agnostic

Every gateway plugs in as a connector behind one type-safe API. Your code never learns which one ran the payment.

Tamper-evident

Writes land in an append-only ledger, hash-chained so any edit to past records is detectable.

05 — Building in the open

v0.3 — honest about scope.

This is an early release. The Iyzico raw-card and stored-card 3-D Secure flows are both verified end-to-end against the gateway sandbox. The API is 0.x and may change before 1.0.

v0.3.0
latest release
Iyzico
sandbox-verified
6
packages shipped
MIT
open source
Verified end-to-endauthorize3DScapturerefundreconcile
Shipped
@orvacon/paykitcore
@orvacon/connector-iyzico
@orvacon/adapter-supabase
@orvacon/adapter-nextjs
@orvacon/cryptokit
orvaconCLI
On the roadmap
PayTR connector
Bank virtual POS connectors
UI components
Subscription · tax · fraud · ledger kits

No "supports every gateway" — these aren't shipped yet, and we won't pretend otherwise.

Built to fit

Tailored to your stack.

Drop it into the app you already have — your framework, your database, your runtime. Install only what you need.

Fits your existing stack

Your framework, your database, your runtime — no proprietary tooling, no vendor lock-in.

BunNext.jsPostgresSupabase

One clean API, any gateway

Your code calls orva.authorize(…) and never knows which gateway handled the payment.

Default-deny, out of the box

Bring your own database. The Supabase adapter generates a schema where row-level security denies by default.

alter table orvacon_payments
  enable row level security;
generate and go
$bunx orvacon generate
wrote supabase/migrations/0001_orvacon.sql
default-deny RLS on payments · ledger · events
$supabase db push
schema applied
06 — Get started

Install only what you need.

A shadcn-style DX: one unscoped CLI, and connectors you opt into. Unused gateways never enter your bundle.

1Install the core, a connector, and your adapters
bun add @orvacon/paykit @orvacon/connector-iyzico @orvacon/adapter-supabase @orvacon/adapter-nextjs postgres
2Generate your Ed25519 webhook signing keys
$ bunx orvacon keys
3Generate the schema with default-deny RLS
$ bunx orvacon generate
07 — Changelog

Shipped in the open.

View all releases
v0.3.0latest release2026

Iyzico 3-D Secure, verified

authorize3DScapturerefundreconcile, end-to-end against the gateway sandbox.

Stored-card tokens

Vault a card behind the gateway, then charge it later by token — sandbox-verified end-to-end.

Ed25519-signed webhooks

Asymmetric signatures so a leaked verification key can't forge events. Timing-safe checks throughout.

Schema with default-deny RLS

Generates a Postgres schema with row-level security locked down out of the box.

Hash-chained ledger

Append-only and tamper-evident by construction; idempotency enforced by a unique constraint.

No held money. No hidden service. Just type-safe code, running in your own runtime.

MIT · v0.3 · Running in your own runtime

Build payments without
the glue code.

Wire up one type-safe API, keep money custody with your own gateway, and ship the verified Iyzico 3-D Secure flow today.