Manual capture = Transaction/Validate: it confirms an
AUTHORISED_TO_VALIDATE transaction for capture on its expected date.
Transaction/Capture is NOT this operation — it is a Brazil-specific batch
WS and must never be used here. Validation releases the full authorized
amount; PayZen has no partial validate, so a differing amount is
rejected. The required idempotencyKey cannot be delegated — PayZen has
no idempotency channel — but replays are naturally safe: validating twice
yields a status error, never a duplicate capture.
Charge/CreatePayment → formToken (the client secret the krypton form mounts with). No transaction exists until the shopper pays, and the formToken expires after ~15 minutes — sessions are cheap to re-create.
PayZen has no idempotency mechanism, so the adapter synthesizes traceable replays: orderId derives deterministically from the idempotencyKey and the key is stamped into metadata. A replayed call mints another formToken for the SAME orderId — harmless (no money moves until the shopper completes the form) and reconcilable via Order/Get.
Throws PayFanoutError (code "invalid_request") on unparseable payloads; maps genuinely unknown-but-valid event types to type "unknown" rather than throwing.
PayZen has NO refund idempotency: a replayed Transaction/Refund creates a
SECOND credit while the total stays within the original, and the refund
request carries no metadata/reference field the adapter could pre-check
against (idempotencyKey cannot be delegated OR reliably synthesized).
Consequences, deliberately encoded here:
Partial refunds go to Transaction/Refund (requires a captured source). Full refunds go through Transaction/CancelOrRefund AUTO, which cancels a not-yet-captured transaction instead — the result is mapped honestly (a cancellation releases the authorization; the shopper was never charged, so the refund "succeeded").
Accepts either a transaction uuid (32-hex, from webhooks/refunds) or a PayFanout pspSessionId (= the derived orderId): the krypton form creates the transaction in-browser, so right after checkout the host may only hold the orderId. The orderId path reads the order and reports its most recent payment (DEBIT) attempt.
A PayZen refund IS a transaction (the CREDIT created by Transaction/ Refund) — polling it is Transaction/Get on the refund uuid. A cancellation outcome (full refund of an uncaptured payment) polls the original DEBIT, whose CANCELLED state still means "funds released".
"Test connection" probe: one single-shot, side-effect-free Charge/SDKTest
call — PayZen's purpose-built connection test, which just echoes the
submitted value back on valid credentials. Transport retries are disabled so
a transient failure surfaces promptly instead of replaying (a "Test
connection" click must not hang for multiples of the timeout). PayZen
selects TEST vs LIVE by the key set, so the probe body carries no mode. The
outcome is classified so a host UI can tell a wrong shopId/password (auth)
from a transient outage (network); it resolves on every path instead of
throwing, and never surfaces the credential.
Webhook handling — same adapter, separate concerns. Both async: future PSPs may need remote key retrieval; the contract stays uniform. MUST operate on the RAW request body bytes/string — re-serializing a parsed body breaks signatures (the conformance suite tests this).
Pre-capture void via Transaction/Cancel; captured transactions come back PSP_075 → invalid_request. The required
idempotencyKeyhas no PayZen channel; a replayed cancel is a PSP_105 state error, never a second effect.