Repository navigation
[Question] How to verify subscriptions on backend with openIAP #151
Original ContentI'm trying to implement subscriptions in my Expo app using openIAP. Thank you for any help! QuestionHow do I verify subscriptions on my backend when a user makes a request that requires a premium account? ContextI'm implementing subscriptions in an Expo app using openIAP. I understand that:
However, I'm unclear about the backend verification flow. What I've FoundI discovered the What I Need Help With
Thank you for any guidance! 🤖 Formatted by gitdog.dev |
Replies: 3 comments 4 replies
|
@timolino13 Your understanding is mostly correct! Here's the server-side validation pattern: Frontend Flow:
Backend Flow:
Example from the codebase: where it demonstrates sending the token to a backend endpoint for verification before granting access to premium features.
**Key Points:**
- **Don't validate locally**: Never trust the `Purchase` object's state on the client side alone
- **Always verify server-side**: The JWS token proves the purchase to your backend
- **Token format**: The `purchaseToken` is a cryptographically signed JWT that can't be forged
- **Expiration handling**: Your backend receives `state: "expired"` if the subscription lapsed, allowing you to revoke access
**Security Best Practice:**
```swift
// Frontend: Send token to backend
let purchaseToken = purchase.purchaseToken
let response = try await backend.validateSubscription(token: purchaseToken)
// Backend (pseudocode):
POST /verify-subscription
Body: { "token": purchaseToken }
Response: { "isValid": true, "state": "entitled" }This ensures users can't fake premium access by modifying local app state. The IAPKit verification endpoint is the single source of truth for subscription validity. 🤖 Generated by gitdog.dev · Have more questions? Tag |
|
Small correction to the Gitdog answer above: the token source is right, but I would not recommend passing the purchase token/JWS from the frontend on every premium API request. In OpenIAP,
In import { Platform } from "react-native";
import {
kitApi,
verifyPurchaseWithProvider,
type Purchase,
} from "expo-iap";
const IAPKIT_API_KEY = "openiap-kit_...";
export async function verifyAndBindPurchase(
purchase: Purchase,
userId: string,
) {
const token = purchase.purchaseToken;
if (!token) throw new Error("Missing purchase token");
const result = await verifyPurchaseWithProvider({
provider: "iapkit",
iapkit: {
apiKey: IAPKIT_API_KEY,
...(Platform.OS === "ios"
? { apple: { jws: token } }
: { google: { purchaseToken: token } }),
},
});
if (result.iapkit?.isValid) {
await kitApi({ apiKey: IAPKIT_API_KEY }).bindUser(token, userId);
}
return result.iapkit;
}For backend-gated premium access, I still recommend making your backend the final authority:
POST https://kit.openiap.dev/v1/purchase/verify
Authorization: Bearer openiap-kit_...
Content-Type: application/jsonApple body: { "store": "apple", "jws": "..." }Google body: { "store": "google", "purchaseToken": "..." }
POST /v1/subscriptions/bind-user/{apiKey}
{ "purchaseToken": "...", "userId": "your-user-id" }
GET /v1/subscriptions/status/{apiKey}?userId=...
GET /v1/subscriptions/entitlements/{apiKey}?userId=...Why
Related docs:
|
|
Closing the loop on this one — everything above still holds as of today, and the two gaps you reported are fixed:
The recommended flow is unchanged: send One new thing that may help when a user writes in about their subscription: the dashboard now has an Orders tab where you can paste an Apple or Google order ID from a customer receipt and see the order plus its subscription status — docs. Marking @hyochan's correction as the answer. Feel free to open a new discussion if anything else comes up. |

Small correction to the Gitdog answer above: the token source is right, but I would not recommend passing the purchase token/JWS from the frontend on every premium API request.
In OpenIAP,
purchase.purchaseTokenis the unified token field:purchaseTokenIn
expo-iap, the current API isverifyPurchaseWithProviderwithprovider: "iapkit"(there is not a separateverifyPurchaseWithIAPKitexport today). If you want to verify with IAPKit directly from an Expo app after purchase/restore, it looks like this: