Skip to content

fix: record cards issued during a concurrent freeze or terminate - #16

Merged
GhostAndrey merged 2 commits into
mainfrom
fix/issuing-race
Oct 17, 2026
Merged

GhostAndrey merged 2 commits into
mainfrom
fix/issuing-race

Conversation

@GhostAndrey

Copy link
Copy Markdown
Owner

Что

Закрывает гонку между выпуском карты и жизненным циклом: заморозка или завершение, попавшие в момент вызова провайдера, больше не теряют уже созданную карту.

  • CompleteIssuance записывает PAN и из состояний Frozen/Terminated, сохраняя состояние оператора — токен не «оживает» в Active; повторное завершение по-прежнему отвергается.
  • Freeze разрешён на брони Issuing, поэтому заморозка во время выпуска не падает.
  • MySQL: переход Issuing → Active выполняется через CASE, состояние оператора не перетирается; запись идёт только пока удерживается issuance_key, строка перечитывается после UPDATE.
  • ReleaseTokenIssuance освобождает любую бронь без карты, но никогда не удаляет уже выданную карту.

Тесты

Новые тесты на домен, оба адаптера (memory, MySQL), сервис карт (эмитент замораживает бронь прямо внутри вызова) и lifecycle (freeze во время выпуска замораживает обе сущности).

Проверка

  • gofmt, go build ./..., go vet ./..., golangci-lint run ./... — чисто.
  • go test -race -count=1 ./... — зелено, включая интеграционные тесты internal/adapter/mysql против MySQL 8.0.

Документация

README и CHANGELOG обновлены под новое поведение выпуска.

Freezing or terminating a card while the provider call was still in flight could lose the issued card: Terminate left the token Terminated but CompleteIssuance then refused to record the PAN, and Freeze failed outright on a reservation.

Domain: CompleteIssuance now only refuses a reservation that already carries a card, so a token moved out of Issuing (Frozen, Terminated) keeps that state while the card is recorded; Freeze accepts an in-flight reservation.

MySQL: completion promotes Issuing -> Active in place with a CASE, preserving any operator state, and only while the issuance key is still held; the row is re-read so callers observe the stored state. Release drops any reservation that never received a card and never deletes a provisioned token.

Tests cover the domain, both adapters, the token service and lifecycle freeze.
@GhostAndrey
GhostAndrey merged commit 656f754 into main Oct 17, 2026
2 checks passed
@GhostAndrey
GhostAndrey deleted the fix/issuing-race branch October 17, 2026 16:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant