Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions packages/eas-cli/schema/metadata-0.json
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,39 @@
"vi": { "$ref": "#/definitions/apple/AppleAppClipLocalizedInfo", "description": "Vietnamese" }
}
},
"AppleInAppPurchase": {
"type": "object",
"additionalProperties": false,
"description": "Basic listing of an In-App Purchase. Localizations, pricing, and review screenshots are intentionally out of scope for this schema iteration.",
"required": ["productId", "referenceName", "type"],
"properties": {
"productId": {
"type": "string",
"description": "Unique product identifier for the IAP. Acts as the primary key when reconciling against App Store Connect.",
"minLength": 1
},
"referenceName": {
"type": "string",
"description": "Internal reference name shown in App Store Connect.",
"minLength": 1
},
"type": {
"enum": [
"CONSUMABLE",
"NON_CONSUMABLE",
"NON_RENEWING_SUBSCRIPTION",
"AUTO_RENEWABLE_SUBSCRIPTION",
"AUTOMATICALLY_RENEWABLE_SUBSCRIPTION",
"FREE_SUBSCRIPTION"
],
"description": "In-App Purchase type."
},
"state": {
"type": "string",
"description": "Read-only review state from App Store Connect. Only populated by `metadata:pull`; ignored on `metadata:push`."
}
}
},
"AppleAgeRatingOverride": {
"enum": [
"NONE",
Expand Down Expand Up @@ -1034,6 +1067,13 @@
"appClip": {
"$ref": "#/definitions/apple/AppleAppClip",
"description": "App Clip metadata. Only applies to apps that ship an App Clip target."
},
"inAppPurchases": {
"type": "array",
"description": "Basic listing of In-App Purchases. Currently limited to a declarative round-trip of productId / referenceName / type. Pull is supported; push only reports diffs because the underlying apple-utils API is read-only for now.",
"items": {
"$ref": "#/definitions/apple/AppleInAppPurchase"
}
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions packages/eas-cli/src/metadata/apple/config/reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
AppleAppClip,
AppleAppClipDefaultExperience,
AppleAppClipLocalizedInfo,
AppleInAppPurchase,
AppleMetadata,
ApplePreviews,
AppleScreenshots,
Expand Down Expand Up @@ -257,4 +258,14 @@ export class AppleConfigReader {
public getAppClipLocalizedInfo(locale: string): AppleAppClipLocalizedInfo | null {
return this.schema.appClip?.defaultExperience?.info?.[locale] ?? null;
}

/**
* Get the desired In-App Purchase listing from config, or null if the
* key is missing entirely. An empty array is preserved as an empty array
* (the user explicitly cleared their list) so callers can distinguish
* "unconfigured" from "intentionally empty".
*/
public getInAppPurchases(): AppleInAppPurchase[] | null {
return this.schema.inAppPurchases ?? null;
}
}
20 changes: 20 additions & 0 deletions packages/eas-cli/src/metadata/apple/config/writer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
AppleAppClipDefaultExperience,
AppleAppClipLocalizedInfo,
AppleAppClipReviewDetail,
AppleInAppPurchase,
AppleMetadata,
ApplePreviews,
AppleScreenshots,
Expand Down Expand Up @@ -241,6 +242,25 @@ export class AppleConfigWriter {
}
}

/**
* Set the In-App Purchase listing. Sorts by `productId` for stable
* `metadata:pull` round-trips and drops the key entirely when there are
* no IAPs to avoid spamming `store.config.json` with empty arrays.
*/
public setInAppPurchases(entries: AppleInAppPurchase[]): void {
if (!entries || entries.length === 0) {
delete this.schema.inAppPurchases;
return;
}
const sorted = [...entries].sort((a, b) => a.productId.localeCompare(b.productId));
this.schema.inAppPurchases = sorted.map(entry => ({
productId: entry.productId,
referenceName: entry.referenceName,
type: entry.type,
...(entry.state ? { state: entry.state } : {}),
}));
}

/** Set per-locale App Clip info (subtitle + header image). */
public setAppClipLocalizedInfo(locale: string, info: AppleAppClipLocalizedInfo): void {
this.schema.appClip = this.schema.appClip ?? {};
Expand Down
4 changes: 3 additions & 1 deletion packages/eas-cli/src/metadata/apple/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { AppClipData } from './tasks/app-clip';
import type { AppInfoData } from './tasks/app-info';
import type { AppReviewData } from './tasks/app-review-detail';
import type { AppVersionData } from './tasks/app-version';
import type { InAppPurchasesData } from './tasks/in-app-purchases';
import type { PreviewsData } from './tasks/previews';
import type { ScreenshotsData } from './tasks/screenshots';

Expand All @@ -18,7 +19,8 @@ export type AppleData = { app: App; projectDir: string } & AppInfoData &
AppReviewData &
ScreenshotsData &
PreviewsData &
AppClipData;
AppClipData &
InAppPurchasesData;

/**
* The unprepared partial apple data, used within the `prepareAsync` tasks.
Expand Down
Loading
Loading