Skin payment webhooks: confirm orders without duplicate delivery
Learn how to verify Skinloop webhooks, deduplicate events, validate order amounts and safely queue fulfillment after payment completion.
A redirect is not proof of payment
A customer returning to a success page tells you where their browser navigated. It does not prove that their skin trade completed or that the requested order is paid. Skinloop’s signed webhooks let your server receive payment events independently of whether the customer keeps the checkout open.
A reliable integration combines that notification channel with an authenticated status lookup for reconciliation. Both paths should feed the same durable, idempotent fulfillment operation. Otherwise, a webhook and a status poll can each deliver the same order.
Create the receiver before registering the endpoint
The webhook receiver is an HTTPS endpoint hosted by your server or store platform. It is not the storefront homepage, and Skinloop does not create the receiving server for you. The merchant registers the platform-provided endpoint in API & Webhooks and securely stores the endpoint’s signing secret when it is shown.
The currently configurable event subscriptions are payment.pending, payment.completed and payment.reverted. The broader lifecycle schema describes other statuses, but you should not assume those are subscribable notifications. Poll checkout status when your application needs an intermediate state that is not an available endpoint subscription.
For multiple stores, keep each installation’s credentials and webhook secret separate. Select the expected installation using your receiver’s trusted routing, then verify the signature before using payload fields to identify a merchant or order.
Verify the exact request bytes
Webhook bodies are JSON, but signature verification uses the exact raw request bytes. Parsing JSON and then re-serializing it can change whitespace or ordering and invalidate the signature. Configure your receiver to retain the raw body and follow the documented signature and timestamp validation rules.
Validate the order, then record the event
- Verify the signature and check that the Skinloop-Event-Id header matches the payload id.
- Deduplicate that authenticated event ID using durable storage with a uniqueness constraint.
- Find the expected local order using the saved merchantOrderId and checkout mapping.
- Check the expected merchant context, payment identity, game, requiredAmount and currency before applying any financial or fulfillment action.
- Validate accepted amount and overpayment values using exact decimal handling, not binary floating-point arithmetic.
- Commit the event and any unique fulfillment job before acknowledging it with a 2xx response.
The customer can legitimately overpay by selecting skins worth more than the checkout price. Compare requiredAmount with the stored order price; do not reject a completed payment solely because amount is greater. A changed required amount or currency, however, is not harmless overpayment and must stop automatic fulfillment.
Use two layers of duplicate protection
Skinloop webhook delivery is at least once. Retries and operational replays can use the same stable event ID. A repeated authenticated event with the same identity and body should receive a successful acknowledgement without applying it again. A conflicting authenticated body under the same ID requires investigation.
Event deduplication is only the first layer. The fulfillment job must also be unique for the order or payment. This prevents a status poll, another accepted notification or a retried background job from delivering a second license, balance credit or shipment.
Do not make an external fulfillment call inside the webhook request and then return success without a durable record. Record and queue the work first; let your worker deliver it with safe retry handling. Pending and held payments should update status, not enqueue delivery.
Require the completion gate and handle reversals
Only a verified payment.completed event with data.status equal to completed and data.fulfillmentAllowed equal to true permits fulfillment. Apply equivalent checks to an authenticated status response. A timer, pending event or browser query parameter cannot authorize delivery.
Subscribe to payment.reverted as well as payment.completed. If a reversal arrives after progress or completion, stop further delivery and reconcile the payment against your order history. Notifications need not arrive in an ideal order, so do not treat a delayed lower state as proof that a completed order should be processed again.