Integration steps
Never expose the API key in browser code. Return URLs are navigation, not proof
of payment. Keep CS2 orders reserved during trade protection.
Prerequisites
In the merchant dashboard:- register the HTTPS origins used by any per-request success and cancellation URLs (as in this example), or save both complete URLs as checkout defaults and omit them from the request,
- create an API key with
checkout:createandcheckout:read, and - configure a public HTTPS webhook endpoint and securely store its
whsec_...signing secret.
Complete integration example
The example below includes order storage, checkout creation, status recovery and fulfillment. Expand it when you are ready to implement the full flow.View the full server-side example
View the full server-side example
1. Persist the order before creating a checkout
Create your local order first. Store integer USD cents, a monotonically increasing attempt number, and the exact idempotency key used for that attempt:2. Create and persist the hosted checkout
This server function reads the expected order, calls the real hosted-checkout endpoint, validates the returned identity, and stores the checkout ID before it returns the redirect URL:201 means Skinloop created the checkout. 200 means the same idempotent
request recovered an existing checkout. Handle both identically.Expose this through an authenticated route in your own application. The
browser calls your route; it never calls Skinloop with the merchant key.3. Redirect the customer
Redirect to the exacthostedUrl returned by your server. Do not construct or
modify it:4. Confirm authoritative completion
Use the signed webhook receiver as the primary path. After authenticating the event, look up the local order withdata.merchantOrderId, verify data.requiredAmount and data.currency exactly
against the stored order, and call the same recordCompletionAndEnqueue
operation shown below. data.amount is the actual accepted skin value and can
be greater than data.requiredAmount; require it to be at least the required
amount and verify data.overpaymentAmount equals the difference. Convert all
three decimal strings to integer cents without binary floating point.Use status polling as recovery when a webhook is delayed. The real status
endpoint returns the same hosted-checkout projection:5. Schedule duplicate-safe fulfillment
recordCompletionAndEnqueue must update the order and insert one durable job in
the same local database transaction. The complete implementation is in
Safe fulfillment. Its unique checkout_id,
merchant_order_id, and idempotency_key constraints make a duplicate webhook
or simultaneous status poll return the existing job.The fulfillment worker runs separately after the handler commits. It sends the
same stable destination idempotency key on every attempt:6. Handle every non-success outcome
For a customer retry after a safe terminal result, increment
checkout_attempt, persist a new idempotency key, and then create a new
checkout. Never replace a pending, active, hold, completed, or
reconciliation-required checkout.If the fulfillment destination times out after it may have delivered the order,
do not create another checkout or blindly deliver again. Query the destination
with the same idempotency key. If its result cannot be determined, set the
fulfillment job to needs_reconciliation and require manual review.End-to-end result
The integration is complete when:- refreshing or retrying checkout creation recovers the same Skinloop checkout,
- the customer is redirected only to the returned
hostedUrl, - redirects never mark an order paid,
- only an authenticated
completedresult withfulfillmentAllowed: trueenqueues fulfillment, - webhook delivery and polling converge on one fulfillment job,
- safe terminal failures release the reservation, and
- ambiguous payment or delivery outcomes stop for reconciliation instead of risking a duplicate order.
