5.1 KiB
5.1 KiB
Apple IAP Protocol (Frontend <-> Backend)
This document defines the Apple In-App Purchase verification and grant contract for Eryao Flutter app.
Protocol verification status:
- Backend route source:
backend/src/v1/payments/router.py - Backend service source:
backend/src/v1/payments/service.py - Backend verifier source:
backend/src/v1/payments/apple_verifier.py - Backend schema source:
backend/src/v1/payments/schemas.py - Current status: aligned
Compatibility strategy
- Current strategy: additive evolution (
backward-compatible). - Breaking change requires explicit migration + rollback notes (
requires-migration). - Apple IAP environment is auto-detected from verified Apple transaction payloads; the verify API is not split by Sandbox/Production.
Route overview
- Verify transaction:
POST /api/v1/payments/apple/transactions/verify - Apple server notification:
POST /api/v1/payments/apple/notifications(server-to-server)
Verify transaction
POST /api/v1/payments/apple/transactions/verify
Verify and grant credits for an Apple IAP transaction.
Authorization: Requires authenticated session. User identity from JWT sub.
Request:
{
"productCode": "new_user_pack",
"appStoreProductId": "com.meeyao.qianwen.new_user_pack",
"transactionId": "2000000123456789",
"signedTransactionInfo": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsIng1YyI6WyJNSUlFTURDQ0E3YWdBd0lCQWdJUWFQeF...",
"appAccountToken": "uuid-or-null"
}
Fields:
productCode(required): string, max 32 chars, product code from packages APIappStoreProductId(required): string, max 128 chars, Apple product IDtransactionId(required): string, max 64 chars, Apple transaction IDsignedTransactionInfo(required): string, JWS signed transaction info from AppleappAccountToken(optional): UUID, app account token for user association
Response (200):
{
"status": "granted",
"productCode": "new_user_pack",
"transactionId": "2000000123456789",
"creditsAdded": 60,
"newBalance": 160,
"ledgerEventId": "payment.apple_iap:2000000123456789"
}
Status values:
granted: Transaction verified and credits addedalready_granted: Transaction already processed for this user
Error codes:
| code | status | meaning |
|---|---|---|
PAYMENT_PRODUCT_NOT_FOUND |
404 | productCode does not exist or is not enabled |
PAYMENT_PRODUCT_MISMATCH |
422 | Client product ID does not match backend/Apple verification result |
PAYMENT_TRANSACTION_INVALID |
422 | Apple signed transaction invalid, signature verification failed, or payload malformed |
PAYMENT_TRANSACTION_REVOKED |
409 | Transaction has been revoked or refunded, grant not allowed |
PAYMENT_TRANSACTION_CONFLICT |
409 | Transaction already processed by another user or in conflicting state |
PAYMENT_STARTER_PACK_INELIGIBLE |
409 | Current email identity has already purchased starter pack |
Apple server notification
POST /api/v1/payments/apple/notifications
Server-to-server notification from Apple for refund/revoke events.
Authorization: None (Apple server origin).
Request:
{
"signedPayload": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsIng1YyI6WyJNSUlFTURDQ0E3YWdBd0lCQWdJUWFQeF..."
}
Response (200): Empty success response.
Behavior:
- Parses notification type and transaction info
- For
REFUND,REVOKE,DID_FAIL_TO_RENEWnotifications, processes refund clawback - Refund reduces user balance by original purchase amount (or remaining balance if insufficient)
Product mapping
Products are configured in backend/src/core/config/static/packages/mapping.yaml:
product_mappings:
new_user_pack:
app_store_product_id: com.meeyao.qianwen.new_user_pack
credits: 60
type: starter
sort_order: 0
enabled: true
Starter pack eligibility
- Starter pack (
type: starter) can only be purchased once per email identity - Backend tracks purchase via
register_bonus_claims.has_purchased_starter_pack - If already purchased, returns
PAYMENT_STARTER_PACK_INELIGIBLE(409)
Environment handling
signedTransactionInfois verified server-side and its Apple-providedenvironmentfield is the source of truth.- Valid environments are
SandboxandProduction. - TestFlight and App Review Sandbox transactions are accepted when Apple signs them as
Sandbox. - App Store Production transactions are accepted when Apple signs them as
Production. - Backend configuration defaults to
apple_iap.environment=auto; it must not force all verifications into one Apple environment.
Ledger integration
-
Successful purchases create a ledger entry with:
change_type: purchasebiz_type: paymentbiz_id: apple_iap_transactions.idmetadata.extcontaining Apple IAP details
-
Refunds create a ledger entry with:
change_type: refundbiz_type: paymentmetadata.ext.original_event_idreferencing original purchase event
Error contract linkage
- All errors use RFC7807 with extension
codeand optionalparams. - Error code registry source:
docs/protocols/common/http-error-codes.md.