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:
- Validate the webhook event (JWT).
- Parse the event.
- Update entitlements or inventory in your system based on the event type.
- Mark the order as fulfilled.
- Return an event response.
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): (Unity Project Id),
upid(Environment Id)envId - Token expiration: Ensure (expiration) has not passed.
exp
Webhook event shape
Unity IAP normalizes events from all supported payment providers into this consistent format. Refer to the following example:Refer to the following fields in webhook events:{ "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 }}
Event types
Unity IAP sends the following webhook event types:order.paid
Unity IAP sends theorder.paidorder.updated
Unity IAP sends theorder.updated- The order being marked as fulfilled.
- A refund being processed.
- Other order modifications.
refundedAmountMicrosdata.totalorder.revoked
Unity IAP sends theorder.revokedrevokedMark orders as fulfilled via API
After granting entitlements on your backend, use the Orders API to mark the order as fulfilled. This updates thefulfilledAtAuthentication
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 theGETGET https://iap.services.api.unity.com/v1/projects/{projectId}/environments/{environmentId}/orders/{orderId}
Order statuses
Status | Description |
|---|---|
| Order has been created but payment has not been completed. Can transition to |
| Payment has been received and confirmed by the payment provider. Can transition to |
| Order has been fulfilled and the player has been rewarded. Can transition to |
| Order failed. This is a terminal state. |
| Order has been revoked. This is a terminal state. |
| Order has been cancelled before payment. This is a terminal state. |
Example response body
BothGETPATCH{ "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 thePATCHPATCH https://iap.services.api.unity.com/v1/projects/{projectId}/environments/{environmentId}/orders/{orderId}
Request body
{ "status": "fulfilled"}
The new status to set for the order. Allowed values: or , its status cannot be changed.
- : Set after granting entitlements to the player. Can only be set on
fulfilledorders.paid - : Set to revoke the player's entitlements (for example, after a refund). Can only be set on
revokedorpaidorders.fulfilled - : Set to cancel the order. Can only be set on
cancelledorders (before payment).created
revokedcancelledReturn an event response
You need to return an event response to indicate the success or failure of processing the event.- response: Indicates successful event processing.
2xx - Non-response: Indicates a failure. Unity IAP will replay the event according to the replay policy.
2xx
Event replay behavior
When your webhook endpoint returns a non-2xx