Your backend receives the purchase receipt or transaction ID from the app and forwards it to Frisbii Media for validation. Frisbii Media validates it server-to-server with Apple and returns an inAppToken.
Two StoreKit versions are supported:
StoreKit v1 — uses a base64-encoded receipt from
SKPaymentTransaction.transactionReceiptStoreKit v2 — uses a transaction ID string from
Transaction.id
StoreKit v1 — Add a purchase
/**
* @Route("/apple/receipts/add", name="AppleReceipt", methods={"POST"})
*/
public function addAppleReceipt(Request $request): Response
{
$content = json_decode($request->getContent(), true);
// The app bundle identifier sent by the app as a request header
$appIdentifier = $request->headers->get('x-app-bundle');
// @see https://api.frisbii-media.com/#tag/App-Stores/operation/addAppleAppStorePurchase
$response = json_decode($client->request('POST', '/appstores/apple/purchases', [
'headers' => ['X-plenigo-token' => eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9],
'json' => [
'appIdentifier' => $appIdentifier,
'receiptData' => $content['receiptData'], // base64-encoded receipt from StoreKit v1
],
])->getBody()->getContents(), true);
// inAppToken identifies this purchase until it is associated with a customer
$inAppToken = $response['inAppToken'];
return new JsonResponse(['inAppToken' => $inAppToken]);
}
StoreKit v2 — Add a purchase
StoreKit v2 uses a transaction ID instead of a receipt blob:
/**
* @Route("/apple/transactions/add", name="AppleTransaction", methods={"POST"})
*/
public function addAppleTransaction(Request $request): Response
{
$content = json_decode($request->getContent(), true);
$appIdentifier = $request->headers->get('x-app-bundle');
// @see https://api.frisbii-media.com/#tag/App-Stores/operation/addAppleAppStorePurchase
$response = json_decode($client->request('POST', '/appstores/apple/purchases', [
'headers' => ['X-plenigo-token' => eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9],
'json' => [
'appIdentifier' => $appIdentifier,
'transactionId' => $content['transactionId'], // transaction ID string from StoreKit v2
],
])->getBody()->getContents(), true);
$inAppToken = $response['inAppToken'];
return new JsonResponse(['inAppToken' => $inAppToken]);
}
Verify an existing Apple purchase
Apple sends server-to-server notifications (App Store Server Notifications) to your backend for subscription events — renewals, cancellations, refunds, billing failures, and so on. Forward each notification to Frisbii Media using the same endpoint to keep the subscription state in sync.
/**
* @Route("/apple/receipts/verify", name="AppleReceiptVerify", methods={"POST"})
*/
public function verifyAppleReceipt(Request $request): Response
{
$content = json_decode($request->getContent(), true);
$appIdentifier = $request->headers->get('x-app-bundle');
// @see https://api.frisbii-media.com/#tag/App-Stores/operation/verifyAppleAppStorePurchase
$response = json_decode($client->request('PUT', '/appstores/apple/purchases/verify', [
'headers' => ['X-plenigo-token' => eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9],
'json' => [
'appIdentifier' => $appIdentifier,
'receiptData' => $content['receiptData'], // StoreKit v1
// 'transactionId' => $content['transactionId'], // StoreKit v2
],
])->getBody()->getContents(), true);
return new JsonResponse($response);
}
Tip: Use the same verify endpoint for both the initial validation callbacks and ongoing renewal callbacks from Apple. The endpoint is idempotent — passing the same receipt twice does not create a duplicate order.
Request payload reference
Field | StoreKit version | Description |
|---|---|---|
| v1 + v2 | Your Apple App Bundle ID (e.g. |
| v1 only | Base64-encoded receipt data from |
| v2 only | Transaction ID string from |