Purchases and prices
Pass prices to the SDK by package ID, run your own purchase with StoreKit or Play Billing, and report purchases that finish elsewhere.
Miniwall renders; it does not charge. Prices and payment come from your app, matched to the screen by package ID. The SDK never sees card data or the amounts you charge.
Package IDs
Each package card in the editor has an ID, such as monthly or annual (see Packages and pricing). Your app gives each ID a price. packageIds lists the IDs a screen uses. The Install SDK page also lists them.
let ids = Miniwall.packageIds(in: paywallJSON) // ["annual", "monthly"]If a screen uses an ID with no price, that card shows without a price.
products
products maps each package ID to a ProductInfo.
| Field | Meaning |
|---|---|
price | In major units: 9.99, not 999. |
currency | ISO 4217 code, such as USD. |
period | Billing period: day, week, month, year, lifetime, or a unit with a count (every 3 months). |
title | Shown by {{ product.store_product_name }}. |
offer | Introductory or trial offer: a price and a period. A free trial is price 0. |
options.products = [
"monthly": ProductInfo(price: 9.99, currency: "USD", period: .every(.month)),
"annual": ProductInfo(price: 59.99, currency: "USD", period: .every(.year),
offer: .init(price: 0, period: .every(.day, count: 7))),
"lifetime": ProductInfo(price: 199, currency: "USD", period: .lifetime),
]Text variables the SDK fills
Screen text can use these (the editor lists them under Price and plan; see Variables). The SDK fills them with the same rules as the Web SDK, and prices are formatted for the screen's language.
| Variable | Value |
|---|---|
product.price | $9.99 |
product.currency_code, product.currency_symbol | USD, $ |
product.period | month, 2 weeks, lifetime |
product.period_abbreviated | mo, 2 wk |
product.period_with_unit | 1 month, 2 weeks |
product.period_in_days, _weeks, _months, _years | Rounded counts (years round down) |
product.periodly | monthly, every 2 weeks |
product.price_per_day, _week, _month, _year | The price converted to that unit |
product.price_per_period | $9.99/month |
product.price_per_period_abbreviated | $9.99/mo |
product.store_product_name | title |
product.relative_discount | 33%. Compares each recurring package's monthly price with the most expensive one. Only for cheaper packages, and only when at least two recurring packages exist. |
product.offer_price, product.offer_period and the other offer_ forms | From offer |
custom.NAME | From customVariables |
Period variables need a period. price_per_* is not set for lifetime. Use variablesPerPackage to override any value as text, per package.
Store prices
Use the price your store reports, so the screen always matches what the user is charged.
Miniwall.products(from:) turns StoreKit 2 Products into products, keyed by your package IDs. Prices, currencies, periods and introductory offers come from the App Store.
let storeProducts: [String: Product] = ["monthly": monthly, "annual": annual]
options.products = Miniwall.products(from: storeProducts)Purchase
Run it in onPurchase. See Callbacks.
The SDK includes a ready StoreKit 2 purchase and restore. They are optional: use your own billing code if you prefer.
options.onPurchase = Miniwall.storeKitPurchase(storeProducts)
options.onRestore = Miniwall.storeKitRestore()storeKitPurchase buys the product of the tapped package, finishes verified transactions and returns false when the user cancels. storeKitRestore syncs with the App Store and returns true when an entitlement is active.
Both stores require a way to restore purchases on screens that sell subscriptions. Pass onRestore: without it the Restore purchases button is hidden.
reportPurchase
If a purchase finishes outside the screen (for example a store transaction that completes later, or a purchase made from another screen of your app), call reportPurchase:
Miniwall.reportPurchase(packageId: "annual")- It credits the screen where the user last tapped buy.
- Without a recent buy tap, it credits the last placement the user met (shown or held out) in the last 30 days. If they were held out it records a holdout purchase.
- Pass
paywallIdto name the screen yourself. - It sends at once. It does nothing when analytics are off or there is nothing to credit.
- With a holdout, call it after every purchase so the groups compare fairly.