Skavio Payment Proof: handle duplicate alerts without duplicate book entries

A repeated payment notification is another observation—not necessarily another payment. Skavio Payment Proof provides noncustodial USDC payment confirmation, evidence and PDF/CSV exports while customers pay the receiving wallet directly. To keep repeated alerts from becoming repeated bookkeeping entries, separate three things: the notification received, the underlying transfer and the bookkeeping action authorized. The workflow below is operational guidance, not a claim that Payment Proof automatically prevents duplicate accounting entries.

1. Understand why event deduplication is not enough

The published Payment Proof webhook contract specifies at-least-once delivery: automatic retries retain the same event and delivery IDs, with up to five attempts. A receiver therefore needs a durable record of accepted event IDs. These are documented contract terms, not an independently tested delivery guarantee. There is a second, less obvious source of double counting. According to the [integration reference](https://getskavio.com/proof/developers/reference), one linked invoice settlement emits both payment.confirmed and invoice.paid. Those are different event types describing related business activity—not two separate receipts. Recognizing an already-seen event ID handles a repeated delivery. It does not prevent two different settlement events from triggering the same bookkeeping action twice. Your workflow must control both notification identity and business-action identity.

2. Keep one payment register, with multiple observations attached

For a manual workflow, maintain a payment register outside the notification inbox. Each underlying payment should have a record connecting its evidence to the relevant business request and any approved bookkeeping entry. When another alert arrives—or the same CSV is imported again—locate that record first. If it is repeated evidence of the same payment, attach the observation and retain its arrival or import reference. Do not create a fresh receipt entry merely because the evidence arrived through another channel. Do not match on amount alone. Two genuine transfers can have the same amount, payer or invoice reference. Preserve a genuinely additional transfer as a separate record for review rather than silently suppressing it, even if it relates to the same request.

3. Approve the bookkeeping action—not the alert

Before authorizing a receipt entry, check the verified transfer facts against the intended request: recipient, token, network, exact amount and confirmation status. If attribution or confirmation remains uncertain, record an exception rather than treating the notification as permission to post. Then establish the business context. A confirmed chain transfer is not, by itself, a customer sale: an owner-funded test or internal treasury movement must not be counted as customer revenue merely because a transfer occurred. Payment evidence and a sales or tax invoice are also separate documents; payment verification does not itself constitute a tax invoice. After an authorized entry is created, write its destination reference back to the register. Later notifications about that payment should lead reviewers to the existing entry. If an external accounting write times out, do not assume it failed. Reconcile the destination record before retrying an ambiguous write; a local deduplication record cannot establish whether a remote posting succeeded.

Developer sidebar: make acceptance durable and effects idempotent

The [published signing instructions](https://getskavio.com/proof/developers) require HMAC-SHA256 over the timestamp, a dot and the untouched request-body bytes; constant-time signature comparison; a five-minute timestamp window; and agreement between the signed body's event ID and its header. Authenticate the notification before trusting its identifiers. The [receiver-owned SQLite example](https://getskavio.com/proof/developers/durable_receiver.py) commits the accepted event ID and a local outbox record together, then returns 204. Its consumer still has to validate the request mapping, exact amount and network, and apply business effects idempotently. Acknowledging durable local acceptance is not the same as completing an accounting write. For EVM-specific transfer inspection, a transaction hash alone is not a universal identifier for an individual token-transfer event: receipts can contain multiple logs, and individual logs have transaction hashes and log indexes. Logs can also be removed during chain reorganizations. The published Payment Proof schema does not expose a transfer log-index field. Do not invent one or present a universal cross-chain transfer key as a supported product field. The developer page explicitly states that deployment and real payment/email end-to-end delivery have not been verified. Treat these examples as designs requiring independent receiver, retry and downstream-posting tests—not proof of production behavior.

4. Test duplicates without hiding real payments

Use synthetic fixtures to test the workflow before relying on it. The acceptance criterion is not simply “fewer entries”: repeated evidence should not produce repeated receipt recognition, while genuinely different transfers must remain visible. These are proposed tests and expected design outcomes, not reported Skavio test results.

Review Skavio Payment Proof and its payment-evidence workflow. →