Documentation

In-App Purchasing

Client API

SDK API

In-App Purchasing

Fulfill purchases through the backend API

Implement your own backend that listens to purchase events and tracks your player's entitlements.
Read time 5 minutesLast updated 13 hours ago

If your game uses server-authoritative entitlements, implement a backend system that does the following:
  1. Validate the webhook event (JWT).
  2. Parse the event.
  3. Update entitlements or inventory in your system based on the event type.
  4. Mark the order as fulfilled.
  5. Return an event response.
This ensures both your system and Unity IAP are in sync.
Important
You must mark every paid order as fulfilled, even though your backend grants the player's entitlements. The player keeps their entitlement either way, but an order that stays in the
paid
state leaves Unity IAP order tracking and analytics incomplete. In hybrid setups that mix the SDK and webhooks, unacknowledged orders can also cause order state to drift between systems.

Validate webhook events (JWT)

To enable validation, Unity IAP signs webhook requests with a JSON Web Token (JWT). Verify the following information for this token in your backend to ensure events are authentic:
  • Authorization header:
    Bearer <token>
  • Signature: Signed with a private key. Fetch and cache the JWKS (public key) to verify:
    https://services.api.unity.com/webhooks/.well-known/jwks.json
  • Issuer:
    https://services.api.unity.com/webhooks/
  • Audience claims (array):
    upid
    (Unity Project Id),
    envId
    (Environment Id)
  • Token expiration: Ensure
    exp
    (expiration) has not passed.
There are libraries for most languages that you can use to validate JWTs.

Webhook event shape

Unity IAP normalizes events from all supported payment providers into this consistent format. Refer to the following example:
{ "id": "018d5e5e-5e5e-7e5e-5e5e-5e5e5e5e5e5e", "version": "1.0.0", "eventType": "order.paid", "time": "2024-01-15T14:30:00Z", "projectId": "018d5e5e-1111-7e5e-5e5e-111111111111", "environmentId": "018d5e5e-2222-7e5e-5e5e-222222222222", "dataType": "order", "data": { "id": "018d5e5e-3333-7e5e-5e5e-333333333333", "playerId": "player_12345", "paymentProvider": "stripe", "paymentProviderResourceId": "cs_test_a1b2c3d4e5f6", "url": "https://checkout.stripe.com/pay/cs_test_a1b2c3d4e5f6", "lineItems": [ { "sku": "com.game.coins_100", "productType": "Consumable", "price": { "amountMicros": 4990000, "currency": "USD" } } ], "total": { "amountMicros": 4990000, "currency": "USD", "refundedAmountMicros": 0 }, "status": "paid", "customReferenceId": "order_xyz_789", "metadata": { "campaign": "summer_sale", "platform": "iOS" }, "createdAt": "2024-01-15T14:25:00Z", "updatedAt": "2024-01-15T14:30:00Z", "paidAt": "2024-01-15T14:30:00Z", "fulfilledAt": null }}
Refer to the following fields in webhook events:

id

string
required
A unique identifier for the webhook event.

version

string
required
The version of the webhook event schema.

eventType

string
required
The type of the event. Refer to Event types for possible values.

time

string
required
The time the event was generated (ISO 8601 format).

projectId

string
required
The ID of the Unity project this order belongs to.

environmentId

string
required
The ID of the Unity environment this order occurred in.

dataType

string
required
The type of data in the event.

data

object
required
The order data object containing details about the purchase.

id

string
required
A unique identifier for the order.

playerId

string
required
The Unity authentication player ID.

paymentProvider

string
required
The payment provider that processed the order, for example,
stripe
or
coda
.

paymentProviderResourceId

string
required
The unique ID assigned to the resource by the payment provider.

url

string
required
The checkout URL for the order.

lineItems

object array
required
A list of products purchased in the order.

sku

string
required
The unique identifier for the product.

productType

string
required
The type of product, for example,
Consumable
.

price

object
required
The price of the product.

amountMicros

integer
required
The price in micros (1,000,000 micros equals $1.00). These values are what was shown to the user.

currency

string
required
The three-letter ISO 4217 currency code.

total

object
required
The total order amount.

amountMicros

integer
required
The total order amount in micros.

currency

string
required
The three-letter ISO 4217 currency code.

refundedAmountMicros

integer
required
The total refunded amount in micros. In partial refunds, this may be smaller than
amountMicros
.

status

string
required
The status of the order. Possible values:
created
,
paid
,
failed
,
fulfilled
,
revoked
.

customReferenceId

string
An optional custom reference ID for the order.

metadata

object
A key-value object containing any custom metadata passed during the purchase flow.

createdAt

string
required
The timestamp when the order was created (ISO 8601 format).

updatedAt

string
required
The timestamp when the order was last updated (ISO 8601 format).

paidAt

string
The timestamp when the order was paid (ISO 8601 format).

fulfilledAt

string
The timestamp when the order was fulfilled, or
null
if not yet fulfilled.

revokedAt

string
The timestamp when the order was revoked, or
null
if not revoked.

Event types

Unity IAP sends the following webhook event types:

order.paid

Unity IAP sends the
order.paid
event when a player successfully completes a purchase. When you receive this event, grant the player the entitlements or currency they purchased and mark the order as fulfilled.

order.updated

Unity IAP sends the
order.updated
event every time the order is updated. This includes changes such as the following:
  • The order being marked as fulfilled.
  • A refund being processed.
  • Other order modifications.
The
refundedAmountMicros
field in
data.total
reflects the current total refunded amount.
Refunds are developer-initiated. For example, you might issue a refund to a player through the payment provider's dashboard. Unity IAP doesn't automatically revoke entitlements when a refund occurs. If you want to revoke entitlements on refund, you can listen for this event and handle it in your own entitlement system.

order.revoked

Unity IAP sends the
order.revoked
event when the order status changes to
revoked
. When you receive this event, revoke the player's entitlements or currency that was granted for the original purchase.
Chargebacks are player-initiated. For example, a player might dispute a charge through their credit card company or bank. When a chargeback dispute closes and the player wins the dispute, Unity IAP automatically revokes the order and sends this event. Unlike refunds, Unity IAP revokes entitlements on chargebacks.

Mark orders as fulfilled via API

After granting entitlements on your backend, use the Orders API to mark the order as fulfilled. This updates the
fulfilledAt
timestamp and ensures the order status is tracked correctly.

Authentication

To call these endpoints, you need to authenticate using one of the following methods:
  • Service account: Create a service account in the Unity Dashboard and use it to authenticate your backend server. For more information, refer to Service account authentication.
  • Cloud Code: Call the endpoint from a Cloud Code script or module, which can authenticate on behalf of your project. For more information, refer to Use a Cloud Code module to fulfill purchases.

Get order

Call the
GET
endpoint
to retrieve the order.
GET https://iap.services.api.unity.com/v1/projects/{projectId}/environments/{environmentId}/orders/{orderId}

Order statuses

Status

Description

created
Order has been created but payment has not been completed. Can transition to
paid
,
failed
, or
cancelled
.
paid
Payment has been received and confirmed by the payment provider. Can transition to
fulfilled
or
revoked
.
fulfilled
Order has been fulfilled and the player has been rewarded. Can transition to
revoked
.
failed
Order failed. This is a terminal state.
revoked
Order has been revoked. This is a terminal state.
cancelled
Order has been cancelled before payment. This is a terminal state.

Example response body

Both
GET
and
PATCH
endpoints return the full order object:
{ "id": "018d5e5e-3333-7e5e-5e5e-333333333333", "projectId": "018d5e5e-1111-7e5e-5e5e-111111111111", "environmentId": "018d5e5e-2222-7e5e-5e5e-222222222222", "playerId": "player_12345", "paymentProvider": "stripe", "paymentProviderResourceId": "cs_test_12345", "url": "https://checkout.stripe.com/pay/cs_test_12345", "lineItems": [ { "sku": "com.game.coins_100", "productType": "Consumable" } ], "status": "paid", "fulfilledAt": null, "revokedAt": null, "customReferenceId": "order_xyz_789", "metadata": { "campaign": "summer_sale" }, "createdAt": "2024-01-15T14:25:00Z", "updatedAt": "2024-01-15T14:30:00Z"}

Update an order

Call the
PATCH
endpoint
to update an order's status:
PATCH https://iap.services.api.unity.com/v1/projects/{projectId}/environments/{environmentId}/orders/{orderId}

Request body

{ "status": "fulfilled"}

status

string
required
The new status to set for the order. Allowed values:
  • fulfilled
    : Set after granting entitlements to the player. Can only be set on
    paid
    orders.
  • revoked
    : Set to revoke the player's entitlements (for example, after a refund). Can only be set on
    paid
    or
    fulfilled
    orders.
  • cancelled
    : Set to cancel the order. Can only be set on
    created
    orders (before payment).
Once an order is
revoked
or
cancelled
, its status cannot be changed.
You can refer to the IAP Client API documentation for the HTTP response status codes.

Return an event response

You need to return an event response to indicate the success or failure of processing the event.
  • 2xx
    response
    : Indicates successful event processing.
  • Non-
    2xx
    response
    : Indicates a failure. Unity IAP will replay the event according to the replay policy.

Event replay behavior

When your webhook endpoint returns a non-
2xx
response, Unity IAP automatically retries delivery of the event. Ensure your webhook handler is idempotent so that replayed events don't result in duplicate entitlements.

Next steps

This page is part of a workflow to set up Direct to Consumer (D2C) payment providers with IAP. To continue this workflow, choose one of the following options: