콘텐츠로 이동

PgBillingKeyStore — @gj-kit/toss-payments-postgresql

@gj-kit/toss-payments-postgresql/nestjs에서 공개하는 interface입니다. package version 0.5.1의 release declaration을 그대로 표시합니다.

import { PgBillingKeyStore } from '@gj-kit/toss-payments-postgresql/nestjs';
/**
* PostgreSQL이 제공하는 BillingKeyStore 확장.
*
* 코어 `BillingKeyStore`도 expected billing key를 받는 조건부 삭제를 강제한다. 이 확장은
* 지연된 `BILLING_DELETED`, projection 보상, 발급 후 host lifecycle을 같은 customerKey
* fence 안에서 끝내야 하는 호출자를 위한 PostgreSQL 전용 API다.
*
* `replaceAndGetPrevious`와 두 conditional 메서드는 하나의 커넥션/트랜잭션에서
* customerKey별 advisory lock → `SELECT … FOR UPDATE` → decrypt → constant-time
* compare → UPSERT/UPDATE/DELETE를 수행한다. 따라서 같은 customerKey의 더 최신
* issuance가 먼저 저장됐다면 conditional 호출은 false를 반환하고, 이 호출이 먼저
* 잠갔다면 뒤의 issuance는 commit 뒤에 실행되어 최신 issuance를 보존한다.
*/
interface PgBillingKeyStore extends BillingKeyStore {
/**
* customerKey별 PostgreSQL advisory transaction lock을 callback 전체에 유지한다.
*
* 같은 customerKey의 generic 저장과 앱 projection을 순서대로 끝내야 할 때의
* cross-instance fence다. 모든 경쟁 issuance/deletion/compensation이 이 API를 사용해야
* 한다. callback 성공 시 commit, throw 시 generic billing key 변경은 rollback된다.
* callback 안에서는 전달된 mutation handle만 사용하고 바깥 store를 재호출하지 않는다.
*/
withMutationLock<T>(customerKey: BillingKeyRecord['customerKey'], operation: (mutation: PgBillingKeyMutation) => T | Promise<T>): Promise<T>;
/**
* opaque lifecycle lock과 customerKey mutation lock을 **같은 PostgreSQL connection과
* transaction**에서 `opaque → customer` 순서로 획득한다.
*
* credential issuance/revocation/compensation처럼 host lifecycle과 generic billing-key
* mutation을 함께 직렬화해야 할 때의 유일한 composable API다. callback은 두 lock을 모두
* 얻은 뒤에만 기존 customer-bound mutation handle을 받는다. callback 안에서는 handle만
* 사용하고 `opaqueLocks.withLock` 또는 outer billing store를 재진입하지 않는다.
*
* `opaqueLocks.withLock(key, () => withMutationLock(...))`처럼 두 public API를 중첩하면
* 서로 다른 `withConnection`을 열어 pool max=1에서 self-deadlock할 수 있고, 한
* transaction이라는 보장도 잃는다. 모든 결합 경로의 global lock order는 이 메서드가
* 강제하는 **opaque → customer**다.
*/
withOpaqueMutationLock<T>(opaqueKey: OpaqueAdvisoryLockKey, customerKey: BillingKeyRecord['customerKey'], operation: (mutation: PgBillingKeyMutation) => T | Promise<T>): Promise<T>;
/**
* record를 저장하고, 같은 트랜잭션에서 잠근 직전 snapshot을 반환한다.
*
* 단일 generic write의 snapshot/보상에는 `find()` 뒤 `save()`보다 안전하다. 다만 앱
* projection까지 순서 보장이 필요하면 이 단독 메서드가 아니라 `withMutationLock` 안의
* 같은 이름 메서드를 사용한다. 그 callback 안에서 반환된 snapshot(첫 발급이면 null)을
* 이후 `replaceIfBillingKeyMatches(record.billingKey, previous)`에 전달하면 현재 값이
* 여전히 record일 때만 원자 복원/삭제할 수 있다. snapshot 원본을 그대로 넘기면 prior
* operation fingerprint까지 보존한다.
*/
replaceAndGetPrevious(record: BillingKeyRecord, options?: BillingKeySaveOptions): Promise<PgBillingKeySnapshot | null>;
/**
* 현재 billing key가 `expectedBillingKey`와 같을 때만 행을 삭제한다.
*
* 행이 없거나 현재 키가 다르면 false이고, 보호 payload 손상/복호화 실패는 숨기지 않고
* throw한다. false는 삭제되지 않았다는 안전한 결과이지 저장소 장애를 뜻하지 않는다.
*/
deleteIfBillingKeyMatches(request: BillingKeyDeleteRequest): Promise<boolean>;
/**
* 현재 billing key가 `expectedBillingKey`와 같을 때만 replacement로 교체한다.
*
* `replacement`가 null이면 조건부 삭제다. 보상 경로에서는 발급 직후 저장한 새 키를
* expected로, 이전 snapshot(또는 첫 발급이면 null)을 replacement로 전달한다. replacement의
* customerKey는 첫 인자와 반드시 같아야 한다.
*/
replaceIfBillingKeyMatches(customerKey: BillingKeyRecord['customerKey'], expectedBillingKey: BillingKeyRecord['billingKey'], replacement: BillingKeyRecord | PgBillingKeySnapshot | null): Promise<boolean>;
}

이 선언은 매개변수, optionality, 제네릭, 반환값, 공개 union/type 계약의 정본입니다. 호출 전 필요한 환경·권한·오류 경계는 패키지 Golden path와 이 subpath의 import 조건을 함께 확인하세요.

  • 패키지: @gj-kit/toss-payments-postgresql
  • 버전: 0.5.1
  • 공개 entry: ./nestjs
  • 소스: GitHub

PostgreSQL이 제공하는 BillingKeyStore 확장.

코어 BillingKeyStore도 expected billing key를 받는 조건부 삭제를 강제한다. 이 확장은 지연된 BILLING_DELETED, projection 보상, 발급 후 host lifecycle을 같은 customerKey fence 안에서 끝내야 하는 호출자를 위한 PostgreSQL 전용 API다.

replaceAndGetPrevious와 두 conditional 메서드는 하나의 커넥션/트랜잭션에서 customerKey별 advisory lock → SELECT … FOR UPDATE → decrypt → constant-time compare → UPSERT/UPDATE/DELETE를 수행한다. 따라서 같은 customerKey의 더 최신 issuance가 먼저 저장됐다면 conditional 호출은 false를 반환하고, 이 호출이 먼저 잠갔다면 뒤의 issuance는 commit 뒤에 실행되어 최신 issuance를 보존한다.