@livx.cc/appwrap 0.53.0 → 0.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -27,6 +27,7 @@ declare const com: any;
|
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
const PERIOD_UNIT = ['D', 'W', 'M', 'Y']; // SKProductPeriodUnit: Day/Week/Month/Year
|
|
30
|
+
const DISCOUNT_FREE_TRIAL = 2; // SKProductDiscountPaymentMode.freeTrial (0=PayAsYouGo, 1=PayUpFront, 2=FreeTrial)
|
|
30
31
|
const STATE = { Purchasing: 0, Purchased: 1, Failed: 2, Restored: 3, Deferred: 4 };
|
|
31
32
|
const SK_ERR_CANCELLED = 2; // SKErrorPaymentCancelled
|
|
32
33
|
|
|
@@ -46,12 +47,40 @@ function appReceipt(): string {
|
|
|
46
47
|
return data ? data.base64EncodedStringWithOptions(0 as unknown as NSDataBase64EncodingOptions) : '';
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
/**
|
|
51
|
+
* SKProductDiscount → the platform-neutral IntroOffer (parity with the Android mapper's zero-price
|
|
52
|
+
* phase, same `{ price, displayPrice, period, isFreeTrial }` shape native-kit's `IntroOffer` types).
|
|
53
|
+
*
|
|
54
|
+
* ⚠️ ELIGIBILITY CAVEAT: StoreKit 1 exposes `introductoryPrice` on the SKProduct REGARDLESS of whether
|
|
55
|
+
* this user is eligible — SK1 has no eligibility API (SK2's `isEligibleForIntroOffer` is Swift-async,
|
|
56
|
+
* unreachable via the ObjC bridge this handler uses). So a user who already consumed a trial in this
|
|
57
|
+
* group MAY see trial copy they won't actually get; StoreKit's own purchase sheet shows the truth at
|
|
58
|
+
* checkout. Gating on real eligibility is a follow-up if/when this handler moves to StoreKit 2.
|
|
59
|
+
*/
|
|
60
|
+
function mapIntroOffer(d: SKProductDiscount, locale: NSLocale): {
|
|
61
|
+
price: number; displayPrice: string; period: string | undefined; isFreeTrial: boolean;
|
|
62
|
+
} {
|
|
63
|
+
const f = NSNumberFormatter.new();
|
|
64
|
+
f.numberStyle = 2; // CurrencyStyle
|
|
65
|
+
f.locale = locale;
|
|
66
|
+
const sp = d.subscriptionPeriod;
|
|
67
|
+
// The discount period is ONE cycle; numberOfPeriods is how many cycles it repeats. Total trial
|
|
68
|
+
// length = numberOfUnits × numberOfPeriods (e.g. 1 week × 1 = P1W).
|
|
69
|
+
const totalUnits = (sp ? sp.numberOfUnits : 0) * (d.numberOfPeriods || 1);
|
|
70
|
+
return {
|
|
71
|
+
price: d.price ? d.price.doubleValue : 0,
|
|
72
|
+
displayPrice: String(f.stringFromNumber(d.price as unknown as number) ?? ''),
|
|
73
|
+
period: sp ? `P${totalUnits}${PERIOD_UNIT[sp.unit] ?? 'D'}` : undefined,
|
|
74
|
+
isFreeTrial: d.paymentMode === DISCOUNT_FREE_TRIAL || (d.price ? d.price.doubleValue : 0) === 0,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
49
78
|
function mapProduct(p: SKProduct) {
|
|
50
79
|
const f = NSNumberFormatter.new();
|
|
51
80
|
f.numberStyle = 2; // CurrencyStyle
|
|
52
81
|
f.locale = p.priceLocale;
|
|
53
82
|
const sp = p.subscriptionPeriod;
|
|
54
|
-
|
|
83
|
+
const product: any = {
|
|
55
84
|
id: String(p.productIdentifier),
|
|
56
85
|
title: String(p.localizedTitle ?? ''),
|
|
57
86
|
description: String(p.localizedDescription ?? ''),
|
|
@@ -62,6 +91,10 @@ function mapProduct(p: SKProduct) {
|
|
|
62
91
|
type: sp ? 'autoRenewable' : 'unknown',
|
|
63
92
|
subscriptionPeriod: sp ? `P${sp.numberOfUnits}${PERIOD_UNIT[sp.unit] ?? 'D'}` : undefined,
|
|
64
93
|
};
|
|
94
|
+
// Surface the intro / free-trial phase so the paywall can show trial terms (see eligibility caveat
|
|
95
|
+
// on mapIntroOffer). Populated only when StoreKit reports an introductory price for this product.
|
|
96
|
+
if (p.introductoryPrice) product.introOffer = mapIntroOffer(p.introductoryPrice, p.priceLocale);
|
|
97
|
+
return product;
|
|
65
98
|
}
|
|
66
99
|
|
|
67
100
|
function receiptFor(productId: string, transactionId?: string) {
|