Skip to content

@gj-kit/toss-payments-postgresql recipes

Each code block reuses a public-contract example already type-checked in the package README.

Adapt the host pool and pass an application-owned protector before exposing any payment store.

Outcome: PostgreSQL-backed Toss stores with one explicit migration step.

Application-owned boundary: Adapt your pool with fromPgPool, use a real encrypted sensitiveValueProtector, and run migration during deployment.

import { createTossPaymentsPostgres, fromPgPool, type PgPoolLike, type SensitiveValueProtector } from '@gj-kit/toss-payments-postgresql';
declare const pool: PgPoolLike;
declare const sensitiveValueProtector: SensitiveValueProtector; // App KMS/encryption boundary.
export const stores = createTossPaymentsPostgres({
sql: fromPgPool(pool),
sensitiveValueProtector,
});
// Run await stores.migrate() once in deployment, never per request.

2. Mutate billing data behind an opaque lock key

Section titled “2. Mutate billing data behind an opaque lock key”

Use the branded opaque key and supplied lock order rather than passing raw customer identifiers to storage.

The protector is a required field and a raw identifier cannot stand in for a lock key — tsc rejects both, at TS2345, before anything reaches a database. withOpaqueMutationLock is also the only API that takes both locks, so the opaque → customer order is not a decision the caller gets to make.

import type { Pool } from 'pg';
import type { BillingKeyRecord } from '@gj-kit/toss-payments/server';
import { createAes256GcmSensitiveValueProtector, createOpaqueAdvisoryLockKey, createTossPaymentsPostgres, fromPgPool } from '@gj-kit/toss-payments-postgresql';
declare const pool: Pool; // app owns the pg Pool
declare const keyHex: string; // app owns key custody and rotation
declare const blindIndex: string; // app owns the blind index of the customer id
declare const record: BillingKeyRecord; // app owns the freshly issued billing key
export const pg = createTossPaymentsPostgres({
sql: fromPgPool(pool),
sensitiveValueProtector: createAes256GcmSensitiveValueProtector({ key: keyHex, keyId: '2026-08' }),
});
// Without sensitiveValueProtector: TS2345 '{ sql: SqlClient; }' is not assignable to 'TossPaymentsPostgresOptions'.
export const previous = await pg.billingKeys.withOpaqueMutationLock(
createOpaqueAdvisoryLockKey(blindIndex), // raw blindIndex: TS2345 'string' is not assignable to 'OpaqueAdvisoryLockKey'
record.customerKey,
(mutation) => mutation.replaceAndGetPrevious(record),
);

Use the complete API reference for every export and import path.