In the first part of the purchase process, your app retrieves information about its products from the App Store, presents its store UI to the user, and then lets the user select a product, as shown in Figure 2-1.
首先,購買產品時,你的應用程序從應用商店獲取該產品的信息,把商店 UI 提供給用戶,然后讓用戶選擇一個產品,如下圖:
Figure 2-1 Stages of the purchase process—displaying store UI
http://wiki.jikexueyuan.com/project/in-app-purchase/images/4.png" alt="" />
一、獲取一個產品識別碼列表
Every product you sell in your app has a unique product identifier. Your app uses these product identifiers to fetch information about products from the App Store, such as pricing, and to submit payment requests when users purchase those products. Your app can either read its list of product identifiers from a file in its app bundle or fetch them from your server. Table 2-1 summarizes the differences between the two approaches.
你在應用程序中出售的每個產品都有一個唯一的產品識別碼。 應用程序使用這些產品識別碼到應用商店獲取產品信息,比如價格,并在用戶購買這些產品時提交支付請求。 應用程序既可以從它的應用束(bundle)的一個文件中讀取該產品識別碼列表,也可以從你的服務器獲取它們。表2-1 描述了這兩種方法的區(qū)別:
Table 2-1 Comparison of approaches for obtaining product identifiers
http://wiki.jikexueyuan.com/project/in-app-purchase/images/5.png" alt="" />
If your app has a fixed list of products, such as an in-app purchase to remove ads or enable functionality, embed the list in the app bundle. If the list of product identifiers can change without your app needing to be updated, such as a game that supports additional levels or characters, have your app fetch the list from your server.
如果你的應用程序有一個固定的產品列表,比如一個內置購買來移除廣告或開啟功能,就 把列表整合到應用束內。 如果產品識別碼列表不需要應用程序更新就可以做改變,比如一個支持額外關卡或角色的游戲,可以從你的服務器獲取列表。
There’s no runtime mechanism to fetch a list of all products configured in iTunes Connect for a particular app. You’re responsible for managing your app’s list of products and providing that information to your app. If you need to manage a large number of products, consider using the bulk XML upload/download feature in iTunes Connect.
沒有運行機制可以為一個特殊的應用獲取 iTunes Connect 中所有的產品列表。你需要負責管理你的應用程序產品列表并提供該信息給你的應用程序。如果你需要管理大量的產品,建議使用 iTunes Connect 中得 bulk XML 上傳下載功能。
1、在應用束中整合產品 IDs
Include a property list file in your app bundle containing an array of product identifiers, such as the following:
在你的應用束中包含了一個特性列表文件,它包括了一個產品識別碼數組。如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<string>com.example.level1</string>
<string>com.example.level2</string>
<string>com.example.rocket_car</string>
</array>
</plist>
To get product identifiers from the property list, locate the file in the app bundle and read it.
要想從 plist 文件中獲取產品識別碼,在應用束中找到該文件并讀取它。
NSURL *url = [[NSBundle mainBundle] URLForResource:@"product_ids"
withExtension:@"plist"];
NSArray *productIdentifiers = [NSArray arrayWithContentsOfURL:url];
Fetching Product IDs from Your Server
2、從服務器獲取產品 IDs
Host a JSON file on your server with the product identifiers. For example:
上傳一個 JSON 文件到你的服務器,文件里帶有產品識別碼:如下:
[
"com.example.level1",
"com.example.level2",
"com.example.rocket_car"
]
To get product identifiers from your server, fetch and read the JSON file as shown in Listing 2-1. Consider versioning the JSON file so that future versions of your app can change its structure without breaking older versions of your app. For example, you could name the file that uses the old structure products_v1.jsonand the file that uses a new structure products_v2.json. This is especially useful if your JSON file is more complex than the simple array in the example.
要想從服務器獲取產品識別碼,如列表2-1所示獲取并讀取該 JSON 文件。 考慮 JSON 文件的版本化,這樣應用程序的未來版本就可以隨時改變它的結構,而不需要打破應用程序的老版本。比如,你可以把使用老結構的產品命名為_v1.json, 把使用新結構的文件命名為_v2.json. 如果你的 JSON 文件比示例中的簡單數組更復雜時,版本化方法更加有用。
Listing 2-1 Fetching product identifiers from your server
- (void)fetchProductIdentifiersFromURL:(NSURL *)url delegate:(id)delegate
{
dispatch_queue_t global_queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(global_queue, ^{
NSError *err;
NSData *jsonData = [NSData dataWithContentsOfURL:url
options:NULL
error:&err];
if (!jsonData) { /* Handle the error */ }
NSArray *productIdentifiers = [NSJSONSerialization
JSONObjectWithData:jsonData options:NULL error:&err];
if (!productIdentifiers) { /* Handle the error */ }
dispatch_queue_t main_queue = dispatch_get_main_queue();
dispatch_async(main_queue, ^{
[delegate displayProducts:productIdentifiers]; // Custom method
});
});
}
For information about downloading files using NSURLConnection, see “Using NSURLConnection” in URL Loading System Programming Guide.
更多關于使用 NSURLConnection 下載文件的信息,請看 “Using NSURLConnection” in URL Loading System Programming Guide.
To ensure that your app remains responsive, use a background thread to download the JSON file and extract the list of product identifiers. To minimize the data transferred, use standard HTTP caching mechanisms, such as the Last-Modified and If-Modified-Since headers.
為了確保你的應用程序保持響應,使用一個后臺線程來下載 JSON 文件,并提取產品識別碼列表。 要想最小化數據轉換,使用標準的HTTP緩存機制,比如 Last-Modified 和 If-Modified-Since 頭。
二、取回產品信息
To make sure your users see only products that are actually available for purchase, query the App Store before displaying your app’s store UI.
為了確保你的用戶只看到真實可買的產品,在顯示應用的商店 UI 之前查詢應用商店。
Use a products request object to query the App Store. First, create an instance of SKProductsRequest and initialize it with a list of product identifiers. The products request retrieves information about valid products, along with a list of the invalid product identifiers, and then calls its delegate to process the result. The delegate must implement the SKProductsRequestDelegate protocol to handle the response from the App Store. Listing 2-2 shows a simple implementation of both pieces of code.
使用一個產品請求對象來查詢應用商店。首先,創(chuàng)建一個 SKProductsRequest 實例,并用產品識別碼列表初始化它。 產品請求取回有效產品的信息,以及一個有效產品識別碼列表,然后調用它的委托來處理結果。 委托必須實現 SKProductsRequestDelegate 協議來處理從應用商店的響應。列表2-2現實了一個兩段代碼的簡單實現。
Listing 2-2 Retrieving product information
// Custom method
- (void)validateProductIdentifiers:(NSArray *)productIdentifiers
{
SKProductsRequest *productsRequest = [[SKProductsRequest alloc]
initWithProductIdentifiers:[NSSet setWithArray:productIdentifiers]];
productsRequest.delegate = self;
[productsRequest start];
}
// SKProductsRequestDelegate protocol method
- (void)productsRequest:(SKProductsRequest *)request
didReceiveResponse:(SKProductsResponse *)response
{
self.products = response.products;
for (NSString *invalidIdentifier in response.invalidProductIdentifiers) {
// Handle any invalid product identifiers.
}
[self displayStoreUI]; // Custom method
}
When the user purchases a product, you need the corresponding product object to create a payment request, so keep a reference to the array of product objects that’s returned to the delegate. If the list of products your app sells can change, you may want to create a custom class that encapsulates a reference to the product object as well as other information—for example, pictures or description text that you fetch from your server. Payment requests are discussed in“Requesting Payment.”
當用戶購買一個產品時,你需要給該產品對象提供一個支付請求,因此保留一個到產品對象數組的引用,該數組會被返回給委托。 如果你的應用出售的產品列表能改變,你或許想要創(chuàng)建一個自定義類來把一個引用和其它信息一起封裝到產品對象中---比如, 圖片或你從服務器獲取的描述文本。 支付請求在“Requesting Payment.” 中討論。
Product identifiers being returned as invalid usually indicates an error in your app’s list of product identifiers, although it could mean the product hasn’t been properly configured in iTunes Connect. Good logging and a good UI help you resolve this type of issue more easily. In production builds, your app needs to fail gracefully—typically, this means displaying the rest of your app’s store UI and omitting the invalid product. In development builds, display an error to call attention to the issue. In both production and development builds, use NSLog to write a message to the console so you have a record of the invalid identifier. If your app fetched the list from your server, you could also define a logging mechanism to let your app send the list of invalid identifiers back to your server.
返回的無效產品識別碼通常表明應用產品識別碼列表中發(fā)生了一個 error, 盡管它或許意味著該產品在 iTunes Connect 中沒有被正確配置。良好的日志(logging)和一個好的 UI 可以幫你更簡單地解決該類型的問題。 在產品構建中,你的應用需要優(yōu)雅地失敗---通常,這意味著顯示剩余的應用 UI 以及忽略無效產品。 在開發(fā)構建中,顯示一個 error 來引起對該問題的關注。在產品和開發(fā)構建中,都使用 NSLog 來編寫一個消息(message)來解決,這樣你就有一個無效識別碼的記錄。 如果你的應用從服務器獲取列表,你或許還要定義一個日志機制來讓你的應用程序把無效識別碼發(fā)送回服務器。
三、呈現你的應用商店UI
Because the design of your app’s store has an important impact on your in-app sales, it’s worth investing the time and effort to get it right. Design the user interface for your store UI so it integrates with the rest of your app. Store Kit can’t provide a store UI for you. Only you know your app and its content well enough to design your store UI in a way that showcases your products in their best light and fits seamlessly with the rest of your app.
因為你的應用的商店設計對你的內置銷售有很重要的影響,因此它值得投入時間和精力讓其順利工作。 為你的商店 UI 設計用戶界面讓它與你的應用的剩余部分集成(integrates)。 商店 Kit 不能給你提供商店 UI。 只有充分了解你的應用和它的內容設計出來的商店 UI,才能以最佳方向展示你得產品并使它與你的應用無縫結合。
Consider the following guidelines as you design and implement your app’s store UI.
當你設計和實現應用商店的 UI 時,請考慮以下指南:
Display a store only if the user can make payments. To determine whether the user can make payments, call the canMakePayments class method of theSKPaymentQueue class. If the user can’t make payments (for example, because of parental restrictions), either display UI indicating that that the store isn’t available or omit the store portion of your UI entirely.
1.只在用戶可以支付時顯示一個商店。 調用 SKPaymentQueue 類 的canMakePayments 方法來確認用戶是否可以支付。 如果用戶不能支付(比如,父母限制), 可以顯示 UI 表明一個商店不可用,或者完全忽略商店部分。
Present products naturally in the flow of your app. Find the best place in your UI to show your app’s store UI. Present products in context at the time when the user can use them—for example, let users unlock functionality when they try to use that premium feature. Pay special attention to the experience a user has when exploring your app for the first time.
2.在你的應用流(flow)中自然地呈現產品。 在你的UI找到最好的位置來顯示你的應用商店 UI。 在用戶可以使用它們的時候呈現產品---比如,當用戶嘗試使用高級功能時讓用戶解鎖該功能。 特別關注用戶第一次使用你的應用時的用戶體驗。
Organize products so that exploration is easy and enjoyable. If your app has a small enough number of products, you can display everything on one screen; otherwise, group or categorize products to make them easy to navigate. Apps with a large number of products, such as comic book readers or magazines with many issues, benefit especially from an interface that makes it easy for users to discover new items they want to purchase. Make the differences between your products clear by giving them distinct names and visuals—if necessary, include explicit comparisons.
3.組織產品,這樣探索就變得既簡單又愉快。 如果你的應用只有一點產品,你可以在一個屏幕上顯示所有產品;否則就把產品分組或分類讓用戶可以容易找到。 帶有大數量產品的應用,比如,漫畫書或者有很多報道的雜志,制作一個可以讓用戶簡單地發(fā)現它們想要購買的新產品會特別有用。 給你的產品一個不同的名字和視覺效果,明確地區(qū)分它們--如有需要,包括明確地比較。
Communicate the value of your products to your users. Users want to know exactly what they’re going to buy. Combine information from the App Store, such as product prices and descriptions, with additional data from your server or the app bundle, such as images or demos of your products. Let users interact with a product in a limited way before buying it. For example, a game that gives the user the option to buy new race cars can allow users to run a test lap with the new car. Likewise, a drawing app that lets the user buy additional brushes can give users the chance to draw with the new brush on a small scratch pad and see the difference between brushes. This kind of design provides users an opportunity to experience the product and be convinced they want to purchase it.
4、向用戶交流你的產品的價值。 用戶想要準確地知道他們即將要買什么。 綜合應用商店的信息,比如產品價格和描述,和服務器或應用束中的附加數據,比如,產品的圖片或 demos。 讓用戶在購買之前以一種限制的方式體現產品。 舉個例子,有一個游戲,讓用戶選擇購買新賽車(race cars)可以讓用戶用新車試跑一圈。 又或者,一個繪圖應用,讓用戶購買一個額外的筆刷可以讓用戶用新筆刷在一個小便簽上繪圖,讓它查看筆刷之間的區(qū)別。 這類的設計給用戶提供了一個體驗產品的機會并說服它們購買。
Display prices clearly, using the locale and currency returned by the App Store. Ensure that the price of a product is easy to find and easy to read. Don’t try to convert the price to a different currency in your UI, even if the user’s locale and the price’s locale differ. Consider, for example, a user in the United States who prefers the United Kingdom locale for its units and date formatting. Your app displays its UI according to the United Kingdom locale, but it still needs to display product information in the locale specified by the App Store. Converting prices to British pounds sterling, in an attempt to match the United Kingdom locale of the rest of the interface, would be incorrect. The user has an App Store account in the United States and pays in U.S. dollars, so prices would be provided to your app in U.S. dollars. Likewise, your app would display its prices in U.S. dollars. Listing 2-3 shows how to correctly format a price by using the product’s locale information.
5、使用應用商店返回的語言環(huán)境(locale)和貨幣清楚地顯示價格。 確保能簡單找到產品的價格并能簡單地識別。 不要嘗試在你的 UI 中把價格轉換為一個不同的貨幣,即使用戶的語言環(huán)境和價格的語言環(huán)境不一樣。 想想看,一個在美國的用戶因為它的單位和日期格式更喜歡英國的語言環(huán)境。 你的應用根據英國的語言環(huán)境顯示它的 UI,但它任然需要用應用商店指定的語言環(huán)境顯示產品信息。 為了嘗試匹配余下界面的英國語言環(huán)境,把價格轉換為英國英鎊是不正確的。 用戶在美國有一個應用商店賬號,它用美元支付,因此會給你的應用提供一個以美元為單位的價格。還有,你得應用將以美元為單元顯示價格。列表2-3 顯示了如何通過使用產品的語言環(huán)境信息來正確設置價格單位。
Listing 2-3 Formatting a product’s price
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[numberFormatter setLocale:product.priceLocale];
NSString *formattedPrice = [numberFormatter stringFromNumber:product.price];
After a user selects a product to buy, your app connects to the App Store to request payment for the product.
當用戶選擇好要購買的產品后,你的應用連接到應用商店請求為該產品提供支付。
四、建議測試步驟
Test each part of your code to verify that you’ve implemented it correctly.
測試代碼的每個部分來認證你已經正確地實現了該功能。
1、用你的測試賬號登入應用商店
Create a test user account in iTunes Connect, as described in “Creating Test User Accounts” in iTunes Connect Developer Guide.
在 iTunes Connect 里創(chuàng)建一個測試賬號,請看 iTunes Connect Developer Guide. 的“Creating Test User Accounts” 。
On a development iOS device, sign out of the App Store in Settings. Then build and run your app from Xcode.
在一個 iOS 開發(fā)設備中,在設置中退出應用商店。然后從 Xcode 構建和運行你的應用。
On a development OS X device, sign out of the Mac App Store. Then build your app in Xcode and launch it from the Finder.
在一個 OS X 開發(fā)設備中,退出 Mac 應用商店。 然后在 Xcode 中構建你的應用,并從 Finder 中運行它。
Use your app to make an in-app purchase. When prompted to sign in to the App Store, use your test account. Note that the text “[Environment: Sandbox]” appears as part of the prompt, indicating that you’re connected to the test environment.
使用你的應該來完成一個內置購買。當提示你登陸應用商店時,使用你的測試賬號。 注意提示框中出現的[Environment: Sandbox]文字提示你目前連接到測試環(huán)境。
If the text “[Environment: Sandbox]” doesn’t appear, you’re using the production environment. Make sure you’re running a development-signed build of your app. Production-signed builds use the production environment.
如果[Environment: Sandbox] 沒有出現,則表示你使用的是產品環(huán)境。 確保你的應用運行在一個開發(fā)簽署(development-signed)構建。簽署的產品構建使用產品環(huán)境。 Important: Don’t use your test user account to sign in to the production environment. If you do, the test user account becomes invalid and can no longer be used.
重要提示:不要用你的測試用戶賬號登陸產品環(huán)境,否則測試用戶賬號將無效不能再使用。
If your product identifiers are embedded in your app, set a breakpoint in your code after they’re loaded and verify that the instance of NSArray contains the expected list of product identifiers.
如果你的產品認證碼被整合進應用程序,在它們加載后,在代碼里設置一個斷點,并認證包含了預期產品識別碼列表的 NSArray 實例。
If your product identifiers are fetched from a server, manually fetch the JSON file—using a web browser such as Safari or a command-line utility such as curl—and verify that the data returned from your server contains the expected list of product identifiers. Also verify that your server correctly implements standard HTTP caching mechanisms.
如果你的產品識別碼是從服務器中獲取的,使用一個網絡瀏覽器如 Safari 或一個命令行工具如 curl 來手工獲取 JSON 文件,并認證從服務器返回的數據,該數據包含了預期的產品識別碼列表。 同時還要認證你得服務器正確地實現了標準 HTTP 緩存機制。
3、測試處理無效產品識別碼
Intentionally include an invalid identifier in your app’s list of product identifiers. (Make sure you remove it after testing.)
在你的產品識別碼列表中故意包含進一個無效識別碼。(確保在測試成功后刪除它)
In a production build, verify that the app displays the rest of its store UI and users can purchase other products. In a development build, verify that the app brings the issue to your attention.
在一個產品構建中,驗證應用程序顯示了其余的商店UI,用戶可以購買其他的產品。 在一個開發(fā)構建中,驗證應用程序中引起你關注的問題。
Check the console log and verify that you can correctly identify the invalid product identifier.
檢查控制臺日志并驗證你可以正確地識別無效產品識別碼。
4、測試一個產品請求
Using the list of product identifiers that you tested, create and submit an instance ofSKProductsRequest . Set a breakpoint in your code, and inspect the lists of valid and invalid product identifiers. If there are invalid product identifiers, review your products in iTunes Connect and correct your JSON file or property list.
使用你測試好的產品識別碼列表,創(chuàng)建并遞交一個SKProductsRequest 實例。 在你的代碼中設置一個斷點,檢查有效和無效的產品識別碼。 如果有無效產品識別碼,檢閱 iTunes Connect 中的產品并更正你的 JSON 文件或特性列表。