Receive payment webhooks.
Try it first
Deliver a callback without double granting#
You need An external HTTPS receiver, raw request bytes and the matching webhook signing configuration.
- Send a signed sample and record delivery; the sample must not grant items.
- For a real test purchase, persist the event in a durable inbox.
- Verify the authoritative receipt and run browser completion plus callback through one grant ledger.
Check your result Duplicate delivery and completion still produce one inventory change.
Receive signed payment events on your backend. Make duplicate delivery safe before you connect fulfillment.
Register your endpoint#
Register a URL through POST /v1/webhook_endpoints with your server’s secret studio key. The service scopes it to a studio-owned game. Configure the webhook signing secret through your trusted onboarding path.
Verify the original bytes#
Read X-Playmos-Signature and preserve the original request body. Import verifyWebhook from @playmos/sdk/server. Parsing and reserializing JSON before verification changes the signed bytes.
Supported event types#
| Event | Action |
|---|---|
| payment.confirmed | Verify the intended purchase and fulfill once. |
| payment.failed | Record failure; do not grant. |
| payout.settled | Reconcile the payout record. |
| refund.processed | Reconcile the refund and entitlement policy. |
Delivery and deduplication#
Use the event ID as a durable deduplication key. The sender retries HTTP delivery within its bounded attempt loop. A repeated event must reuse the same fulfillment record.
Registration and studio binding#
curl --fail-with-body -X POST https://api.sandbox.playmos.io/v1/webhook_endpoints \-H "Authorization: Bearer $PLAYMOS_SECRET" \-H 'Content-Type: application/json' \-d '{"gameId":"game_sandbox_iap","url":"https://your-server.example/api/store/webhook"}'
Store the returned endpoint.gameId and signingSecret securely. Inspection via GET /v1/webhook_endpoints does not return the secret again. Registration returns 503 webhook_signing_unconfigured when the service operator has not configured its signing secret.
The current sandbox uses a service-level signing secret across studios. Because studios share that sandbox signing key, a valid HMAC alone does not uniquely authenticate Playmos or establish purchase ownership. Verify the payment under your studio key and match your stored purchase before fulfillment.
The signature format is t=<unixSeconds>,v1=<hexHmac>, signing the exact t + '.' + rawBody bytes. Deduplicate event IDs for delivery processing and payment IDs for item grants. payout.settled is a supported event type; it does not make the stubbed bank off-ramp available.
Test a receiver from the Hub#
The local-preview Developer Console can send a signed payment.failed sample to an endpoint registered there. It uses a unique hub_test_ ID and metadata.hubTest: true; it carries no purchase and must never grant an item. The portal reports actual HTTP acceptance, timeout or failure separately from service delivery history. A 2xx response does not prove fulfillment or a real payment.
Test destinations must resolve to public HTTPS addresses on port 443. Redirects and private networks are blocked. The test sender makes one attempt and allows one test per studio every 10 seconds. It is a Hub tool, not a new SDK webhook test API.
Record the sample event once and inspect the actual delivery response. The sample carries no purchase and must never grant an item.
If it did not work#
Reject invalid signatures. A shared sandbox signer alone does not establish event authority; verify the studio-owned receipt.
Try one variation Replay the same saved callback, then alter its raw bytes in an isolated test. The duplicate must not regrant; the altered payload must fail signature verification.
Continue with the next guide →