Overview
Starting with iOS 11, Apple offers Promote in-app purchase on the App Store. This means that in-app purchase items, instead of being purchased within the app, can also be "promoted" on the product page on the App Store.
Users can choose to purchase items directly, subcribe the subscriptions, and navigate to the payment interface within the app. Users will be asked to download and install the app if it is not already installed.
Promote in-app purchase requires 2 basic steps:
- Upload photos for the items, subscription in-app purchase that you want to display on App Store Connect. We can use the App Store Promotions tool in App Store Connect to manage and change their order when appearing on the App Store.
- Implement delegate method of SKPaymentTransactionObserver protocol to handle purchases.
Guildline for marketing in-app purchases on the App Store can be found at Promoting Your In-App Purchases .
To customize the list of promoted in-app purchases, we can use SKProductStorePromotionController to override the App Store Connect default settings such as hiding, showing and order of items. These override settings are separate for each device and they are not synchronized with the iCloud account. Note that this advanced feature depends on the device memory capacity and its API only works after the app has been started at least once. It is not required to implement SKProductStorePromotionController
to promote in-app purchases to the App Store.
Complete a Purchase
When a user chooses to purchase an in-app purchase item on the App Store, StoreKit will automatically open your app and send transaction information via the SKPaymentTransactionObserver
protocol delegate methods. Our job is to continue the purchase transaction and complete the rest of the transaction. For example: authentication, coin addition, unlock level, item …
In the delegate method, return true
to continue the transaction, return false
to suspend or cancel the transaction.
If the app has not been installed, then when tapping on the in-app purchase item, the App Store will automatically ask the user to download and install it (if the app costs money, it will require a purchase). If the app is already installed but the current version of the app is not yet available for this promotion in-app purchase, the App Store will ask the user to upgrade the app to the latest version.
Continue a Transaction
To continue an in-app purchase transaction, we only need to implement the paymentQueue(_:shouldAddStorePayment:for:)
delegate method paymentQueue(_:shouldAddStorePayment:for:)
of the SKPaymentTransactionObserver
protocol and return true
. StoreKit will display the payment page and then the user can complete the normal transaction like a traditional in-app purchase.
1 2 3 4 5 6 7 8 9 10 11 |
<span class="token comment">// Tiếp tục transaction từ App Store</span> <span class="token comment">//MARK: - SKPaymentTransactionObserver</span> <span class="token keyword">func</span> <span class="token function">paymentQueue</span> <span class="token punctuation">(</span> <span class="token number">_</span> queue <span class="token punctuation">:</span> <span class="token builtin">SKPaymentQueue</span> <span class="token punctuation">,</span> shouldAddStorePayment payment <span class="token punctuation">:</span> <span class="token builtin">SKPayment</span> <span class="token punctuation">,</span> forProduct product <span class="token punctuation">:</span> <span class="token builtin">SKProduct</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token builtin">Bool</span> <span class="token punctuation">{</span> <span class="token comment">// Check xem có thể hoàn thành transaction hay không?</span> <span class="token comment">// Return true nếu có thể.</span> <span class="token keyword">return</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> |
Defer or Cancel a Transaction
If you need to suspend or cancel the transaction, return false
. For example, when we cannot connect to the server of the app (unable to authenticate transactions, service unavailable …), we need to postpone this transaction and continue when the server is operational. Or when the user has already unlocked the item, already subscribed to that subscription, then the transaction needs to be canceled.
To postpone a transaction:
- Save the payment you need to pause to continue when the app is ready.
SKPayment
contains information about products, cannot create newSKPayment
instance. - Return
false
. - After completing the transaction, add the saved payment back to the payment queue to continue the transaction as usual.
To cancel a transaction:
- Return
false
. - Give feedback, notify the user. Although this step is not mandatory, if not inform the user, your app will lack action, not responding after the user tap on in-app purchase in the App Store. This looks like a bug, which will make the user experience not good.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<span class="token comment">// Handle transaction từ App Store</span> <span class="token comment">//MARK: - SKPaymentTransactionObserver</span> <span class="token keyword">func</span> <span class="token function">paymentQueue</span> <span class="token punctuation">(</span> <span class="token number">_</span> queue <span class="token punctuation">:</span> <span class="token builtin">SKPaymentQueue</span> <span class="token punctuation">,</span> shouldAddStorePayment payment <span class="token punctuation">:</span> <span class="token builtin">SKPayment</span> <span class="token punctuation">,</span> forProduct product <span class="token punctuation">:</span> <span class="token builtin">SKProduct</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token builtin">Bool</span> <span class="token punctuation">{</span> <span class="token comment">// ... Check nếu như app không đủ điều kiện để hoàn thành transaction, phải tạm hoãn</span> <span class="token keyword">let</span> shouldDeferPayment <span class="token operator">=</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token comment">// Nếu như phải tạm hoãn transaction, lưu lại payment và return false</span> <span class="token keyword">if</span> shouldDeferPayment <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> savedPayment <span class="token operator">=</span> payment <span class="token keyword">return</span> <span class="token boolean">false</span> <span class="token punctuation">}</span> <span class="token comment">// ... Check nếu như phải hủy bỏ transaction</span> <span class="token keyword">let</span> shouldCancelPayment <span class="token operator">=</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token comment">// Nếu như phải hủy bỏ transaction, return false:</span> <span class="token keyword">if</span> shouldCancelPayment <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token boolean">false</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// (Nếu như phải hủy bỏ transaction, đưa ra message thông báo tới user)</span> <span class="token comment">// Tiếp tục transaction đã lưu khi đủ điều kiện thanh toán tiếp transaction</span> <span class="token builtin">SKPaymentQueue</span> <span class="token punctuation">.</span> <span class="token keyword">default</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">add</span> <span class="token punctuation">(</span> savedPayment <span class="token punctuation">)</span> |
Visibility Settings
Get Visibility Settings
To get visibility settings for a product, use the fetchStorePromotionVisibility(for:completionHandler:)
and pass in the product information.
1 2 3 4 5 6 7 8 |
<span class="token comment">// Get visibility settings của một promoted in-app purchase product</span> <span class="token comment">// Fetch product info</span> <span class="token keyword">let</span> storePromotionController <span class="token operator">=</span> <span class="token builtin">SKProductStorePromotionController</span> <span class="token punctuation">.</span> <span class="token keyword">default</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> storePromotionController <span class="token punctuation">.</span> <span class="token function">fetchStorePromotionVisibility</span> <span class="token punctuation">(</span> forProduct <span class="token punctuation">:</span> hiddenBeaches <span class="token punctuation">,</span> completionHandler <span class="token punctuation">:</span> <span class="token punctuation">{</span> visibility <span class="token punctuation">:</span> <span class="token builtin">SKProductSTorePromotionVisiblity</span> <span class="token punctuation">,</span> error <span class="token punctuation">:</span> <span class="token builtin">Error</span> <span class="token operator">?</span> <span class="token punctuation">)</span> <span class="token keyword">in</span> <span class="token comment">// visibility == .default</span> |
Override Visibility Settings
For each different device, we can decide when the in-app purchase item is hidden or visible. For example, you want to hide items that a user has purchased and show only items that they can currently buy.
To do this, first, fetch the product info and update the default store promotion controller with the .hide
setting, as shown below. This product will no longer appear on the App Store for this device.
1 2 3 4 5 6 7 8 9 |
<span class="token comment">// Update visibility settings của một promoted in-app purchase product</span> <span class="token comment">// Fetch product info</span> <span class="token keyword">let</span> storePromotionController <span class="token operator">=</span> <span class="token builtin">SKProductStorePromotionController</span> <span class="token punctuation">.</span> <span class="token keyword">default</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> storePromotionController <span class="token punctuation">.</span> <span class="token function">update</span> <span class="token punctuation">(</span> storePromotionVisibility <span class="token punctuation">:</span> <span class="token punctuation">.</span> hide <span class="token punctuation">,</span> forProduct <span class="token punctuation">:</span> proSubscription <span class="token punctuation">,</span> completionHandler <span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token punctuation">(</span> error <span class="token punctuation">:</span> <span class="token builtin">Error</span> <span class="token operator">?</span> <span class="token punctuation">)</span> <span class="token keyword">in</span> <span class="token comment">// Complete</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> |
In this example, visibility is set by .default
to .default
. Whether the product is hidden or currently based on the default setting you have set on the App Store Connect.
Order of Promoted Products
Override the Order of Promoted Products
Similar to visibility settings, for each different device, we can also change the display order of promoted in-app purchase items on the App Store. For example, in your game app, the user has passed level 10, you can change the order, bring the level's items to the top of the list so that the user can easily see and buy them.
To override the default product order, initialize the array of products in the order you want and pass the update(storePromotionOrder:completionHandler:)
method update(storePromotionOrder:completionHandler:)
. The products in this array will appear in the correct order at the top of the list, followed by the rest of the products you have set in the App Store Connect.
1 2 3 4 5 6 7 8 9 10 11 12 |
<span class="token comment">// Update thứ tự hiển thị của các promoted in-app purchase product</span> <span class="token comment">// Fetch product info của các product cần re-order</span> <span class="token keyword">let</span> storePromotionController <span class="token operator">=</span> <span class="token builtin">SKProductStorePromotionController</span> <span class="token punctuation">.</span> <span class="token keyword">default</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token comment">// Thay đổi thứ tự theo ý muốn</span> <span class="token keyword">let</span> newProductsOrder <span class="token operator">=</span> <span class="token punctuation">[</span> hiddenBeaches <span class="token punctuation">,</span> proSubscription <span class="token punctuation">,</span> fishingHotSpots <span class="token punctuation">]</span> storePromotionController <span class="token punctuation">.</span> <span class="token function">updateStorePromotionOrder</span> <span class="token punctuation">(</span> newProductsOrder <span class="token punctuation">,</span> completionHandler <span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token punctuation">(</span> error <span class="token punctuation">:</span> <span class="token builtin">Error</span> <span class="token operator">?</span> <span class="token punctuation">)</span> <span class="token keyword">in</span> <span class="token comment">// Complete</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> |
Cancel Order Overrides
To cancel custom orders, we simply need to pass an empty array to the update(storePromotionOrder:completionHandler:)
method update(storePromotionOrder:completionHandler:)
. The in-app purchase item list will be displayed in the default order.
Fetch Order Overrides
To get the product order of the current device, use the fetchStorePromotionOrder(completionHandler:)
method. We will get an array of products in the current order. If this method returns an empty array, this means that the products are being displayed in the default order.
1 2 3 4 5 6 7 8 |
<span class="token comment">// Lấy thứ tự hiển thị của các promoted in-app purchase product</span> <span class="token keyword">let</span> storePromotionController <span class="token operator">=</span> <span class="token builtin">SKProductStorePromotionController</span> <span class="token punctuation">.</span> <span class="token keyword">default</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> storePromotionController <span class="token punctuation">.</span> <span class="token function">fetchStorePromotionOrder</span> <span class="token punctuation">(</span> completionHander <span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token punctuation">(</span> products <span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token builtin">SKProduct</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> error <span class="token punctuation">:</span> <span class="token builtin">Error</span> <span class="token operator">?</span> <span class="token punctuation">)</span> <span class="token keyword">in</span> <span class="token comment">// products == [hiddenBeaches, proSubscription, fishingHotSpots</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> |
Source article: Promoting In-App Purchases