play products: clamp regional prices to Play's min/max, per-product results; apple iap: skip live localizations, per-product results
This commit is contained in:
parent
882255a32f
commit
b0641c1fc8
12 changed files with 456 additions and 69 deletions
|
|
@ -19,6 +19,7 @@ import { confirm, need } from '../lib/ctx';
|
|||
import { done, log, step, warn } from '../lib/log';
|
||||
import { allowPriceChanges, type HeldPrice, heldSection, holdPriceChanges } from '../lib/price-gate';
|
||||
import { type Money, toMoney } from '../lib/money';
|
||||
import { applyLimits, clampText, learnLimit, loadLimits, parseLimitError, saveLimits } from '../lib/play-limits';
|
||||
import {
|
||||
allSubscriptions, basePlanId, offerId, type PlayOfferLike, playOfferBody, playOfferDiff, playPatchSubscription, type PlaySubscription, playSubDiff, playSubRegions,
|
||||
playSubscriptionBody, playSubTextChanges,
|
||||
|
|
@ -58,13 +59,15 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
return { convertedRegionPrices: conv.convertedRegionPrices, convertedOtherRegionsPrice: conv.convertedOtherRegionsPrice, regionVersion: conv.regionVersion ?? { version: '2022/02' } };
|
||||
};
|
||||
|
||||
const limits = loadLimits(root);
|
||||
const skipOut = ctx.args.has('skip-out-of-range-regions');
|
||||
const prune = ctx.args.has('prune-languages');
|
||||
// the offer the config would create (pay-up-front prices come from convertRegionPrices of the offer price)
|
||||
const buildOffer = async (s: typeof subs[number], regions: { regionCode: string; price: Money }[], o: { type: string; usd?: number }, extra?: NonNullable<typeof s.offers>[number]) => {
|
||||
let offerRegions;
|
||||
if (o.type === 'pay-up-front') {
|
||||
const oc = await convert(o.usd!);
|
||||
offerRegions = { regions: Object.values(oc.convertedRegionPrices).map(r => ({ regionCode: r.regionCode, price: r.price })), other: oc.convertedOtherRegionsPrice };
|
||||
offerRegions = { regions: applyLimits(Object.values(oc.convertedRegionPrices).map(r => ({ regionCode: r.regionCode, price: r.price })), limits, skipOut).regions, other: oc.convertedOtherRegionsPrice };
|
||||
}
|
||||
return playOfferBody(pkg, s, regions, offerRegions, extra);
|
||||
};
|
||||
|
|
@ -73,7 +76,9 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
const work: { s: typeof subs[number]; body: PlaySubscription; diff: ReturnType<typeof playSubDiff>; version: string; offerMissing: boolean; extraMissing: string[]; offerDrift: OfferDrift[] }[] = [];
|
||||
for (const [i, s] of subs.entries()) {
|
||||
const conv = await convert(s.usd);
|
||||
const regions = playSubRegions(s, conv.convertedRegionPrices, csv);
|
||||
const lim = applyLimits(playSubRegions(s, conv.convertedRegionPrices, csv), limits, skipOut);
|
||||
const regions = lim.regions;
|
||||
for (const c of lim.clamps) warn(TAG, `${s.id}: price limit ${clampText(c)}${skipOut ? '' : ' (clamped; --skip-out-of-range-regions leaves such regions out)'}`);
|
||||
const body = playSubscriptionBody(pkg, s, config.languages, regions, conv.convertedOtherRegionsPrice);
|
||||
const cur = existing.get(s.id) ?? null;
|
||||
const diff = playSubDiff(cur, body);
|
||||
|
|
@ -135,7 +140,9 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
if (!liveOk && priced.length) log(TAG, 'writing what is new; price changes of existing base plans and offers wait for the owner (--allow-price-changes)');
|
||||
const held: HeldPrice[] = [];
|
||||
|
||||
const failedSubs: string[] = [];
|
||||
for (const { s, body, diff, version, offerMissing, extraMissing, offerDrift } of todo) {
|
||||
try {
|
||||
const v = encodeURIComponent(version);
|
||||
const id = encodeURIComponent(s.id);
|
||||
const cur = existing.get(s.id);
|
||||
|
|
@ -186,7 +193,23 @@ export async function run(ctx: Ctx): Promise<void> {
|
|||
await play.call('PATCH', `${offerUrl}/${encodeURIComponent(d.offerId)}?updateMask=phases®ionsVersion.version=${v}`, d.body);
|
||||
log(TAG, `${s.id}: offer ${d.offerId} updated (${[...d.phase, ...(d.prices.length ? [`${d.prices.length} price(s)`] : [])].join('; ')})`);
|
||||
}
|
||||
} catch (e) {
|
||||
const msg = (e as Error).message.replace(/\s+/g, ' ').slice(0, 300);
|
||||
const le = parseLimitError(msg);
|
||||
const cur = le ? body.basePlans?.[0]?.regionalConfigs?.find(r => r.regionCode === le.region)?.price?.currencyCode : undefined;
|
||||
if (le && cur && learnLimit(limits, le, cur)) {
|
||||
saveLimits(root, limits);
|
||||
warn(TAG, `${s.id}: Play limit for ${le.region} is ${le.min}-${le.max}, saved in .storebound/play-price-limits.json: run again and that region is ${skipOut ? 'left out' : 'clamped'}`);
|
||||
}
|
||||
warn(TAG, `${s.id}: FAILED: ${msg}; going on with the next subscription`);
|
||||
failedSubs.push(s.id);
|
||||
}
|
||||
}
|
||||
holdPriceChanges(held);
|
||||
if (failedSubs.length) {
|
||||
process.exitCode = 1;
|
||||
warn(TAG, `${failedSubs.length} of ${todo.length} subscription(s) failed (${failedSubs.join(', ')}); the others are written. Fix the cause, run --dry, then again with --yes.`);
|
||||
return;
|
||||
}
|
||||
done(TAG, held.length ? 'subscriptions done, price changes left for the owner' : 'subscriptions done');
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue