最近完成了蘋果的兩個(gè)關(guān)于訂閱的優(yōu)惠開發(fā):推介促銷優(yōu)惠和訂閱優(yōu)惠. 整理一篇文檔
已換工作,不在更新
本文主要介紹的以下幾個(gè)方面
促銷優(yōu)惠和訂閱優(yōu)惠的基本概念以及開發(fā)流程
開發(fā)中遇到的各種坑
本文的前提是讀者已經(jīng)了解過蘋果內(nèi)購(gòu).
基礎(chǔ)概念
推介促銷優(yōu)惠
可以為自動(dòng)續(xù)期訂閱設(shè)定的具有指定時(shí)限和類型(隨用隨付、提前支付、免費(fèi))的推介促銷折扣價(jià),此價(jià)格面向新顧客。推介促銷優(yōu)惠可用于吸引新顧客。
請(qǐng)謹(jǐn)記:
顧客可以享受每個(gè)訂閱群組的一個(gè)推介促銷優(yōu)惠
您可以針對(duì)每個(gè)地區(qū)設(shè)置一個(gè)當(dāng)前推介促銷優(yōu)惠和一個(gè)未來推介促銷優(yōu)惠
您可以在 App Store Connect 中管理地區(qū)銷售范圍、開始和結(jié)束日期
如果您已推廣您的 App 內(nèi)購(gòu)買項(xiàng)目,推介促銷優(yōu)惠將顯示在您的 App Store 產(chǎn)品頁(yè)上
推介促銷優(yōu)惠適用于運(yùn)行 iOS 10、Apple TVOS 10 和 macOS 10.12.6 及更高版本的顧客
推介促銷的付款方式有三種類型
支付方式

哪些用戶可以享有推介促銷優(yōu)惠?
兩種用戶
全新的appid
購(gòu)買過這個(gè)訂閱群組的商品但是從來沒有享受過促銷優(yōu)惠的appid都可以享受一次優(yōu)惠.
如下圖

由于控制權(quán)完全就在蘋果那里控制, 導(dǎo)致用戶可以更換appID然后無限次以推介促銷優(yōu)惠價(jià)格購(gòu)買商品, 為了解決這個(gè)問題當(dāng)初想了兩種方案:
方案A,用戶可以購(gòu)買,蘋果也會(huì)扣費(fèi), 但是我們不給下發(fā)會(huì)員, 但是會(huì)計(jì)擔(dān)心用戶回去消費(fèi)者協(xié)會(huì)投訴, 所以就放棄了.
方案B,創(chuàng)建了兩個(gè)一個(gè)月的商品, 一個(gè)有推介促銷優(yōu)惠, 另外一個(gè)沒有推介促銷優(yōu)惠.根據(jù)不同的用戶下發(fā)不同的商品, 這樣就可以防止被薅羊毛了, 當(dāng)然這也為后來埋下了一個(gè)天坑, 在下面會(huì)詳細(xì)解釋一下
由于這個(gè)優(yōu)惠策略的局限性, 后來蘋果后續(xù)推出了訂閱優(yōu)惠
訂閱促銷優(yōu)惠
在2019年3月, 隨著iOS12.2 一起發(fā)布了一種新的促銷優(yōu)惠: 訂閱優(yōu)惠(Subscribe Offer)
提供自動(dòng)續(xù)期訂閱的App可向現(xiàn)有顧客和曾訂閱過的顧客提供限時(shí)折扣價(jià)格。提供訂閱優(yōu)惠有助于您贏回曾取消訂閱的顧客,或推動(dòng)顧客以特價(jià)升級(jí)至更高等級(jí)的訂閱。已使用過推介促銷優(yōu)惠的顧客也可以享受該優(yōu)惠。
訂閱優(yōu)惠其實(shí)就是一種優(yōu)惠券的功能, 可以給任何一個(gè)商品新建優(yōu)惠券, 每個(gè)商品可以有多種優(yōu)惠等級(jí)的優(yōu)惠券, 只可以把優(yōu)惠券給曾經(jīng)買過對(duì)應(yīng)商品的用戶使用, 讓他們以優(yōu)惠價(jià)購(gòu)買對(duì)應(yīng)商品, 一個(gè)用戶可以使用多次優(yōu)惠券購(gòu)買(具體幾次由產(chǎn)品策略控制, 這也就是這個(gè)策略最靈活的地方)
可以看看2019年WWDC蘋果提供的Session 305訂閱優(yōu)惠最佳實(shí)踐
比較一下兩種優(yōu)惠

開發(fā)
推介促銷優(yōu)惠
這個(gè)開發(fā)量很少, 客戶端沒有開發(fā)量, 主要是在api那邊. 客戶端只需要在ITunes connect 后臺(tái)配置一下推介促銷優(yōu)惠信息, 然后api那邊根據(jù)憑證里面的兩個(gè)字段is_in_intro_offer_period(是否享受折扣)或者is_trial_period(是否免費(fèi)) 字段就可以判斷當(dāng)前用戶的購(gòu)買是否享受到了優(yōu)惠策略, 方便和財(cái)務(wù)對(duì)賬
ITunes connect 配置流程: 我的app->App內(nèi)購(gòu)買項(xiàng)目->內(nèi)購(gòu)商品->推介促銷->選擇國(guó)家地區(qū)->開始結(jié)束日期->選擇價(jià)格->確認(rèn)

訂閱促銷優(yōu)惠
這個(gè)工作量稍微多一些了, 需要客戶端,api協(xié)同開發(fā)
主要分工
客戶端:
在ITunes connect 后臺(tái)配置促銷優(yōu)惠
負(fù)責(zé)拿著優(yōu)惠券去蘋果端支付
1. 創(chuàng)建秘鑰
去ITunes Connect->用戶和訪問->訂閱->秘鑰

這個(gè)秘鑰要注意下, 只能下載一次, 丟了需要重新申請(qǐng)
2. 創(chuàng)建促銷優(yōu)惠
ITunes connect 配置流程
我的app->App內(nèi)購(gòu)買項(xiàng)目->內(nèi)購(gòu)商品->促銷促銷->促銷名稱和優(yōu)惠產(chǎn)品代碼->支付方式->價(jià)錢和時(shí)限->確認(rèn)

3. 和蘋果交互
這里就很簡(jiǎn)單了, 給SKMuablePAyment的SKPaymentDiscount賦值
- 向API申請(qǐng)
sign
// Fetch the signature from your server to be applied to the offer.
// At this point you know the user and the product the offer is for, and which offer you want to display.
public func prepareOffer(usernameHash: String, productIdentifier: String, offerIdentifier: String, completion: (SKPaymentDiscount) -> Void) {
// Make a secure request to your server, providing the username, product, and discount data
// Your server will use these values to generate the signature and return it, along with the nonce, timestamp, and key identifier that it uses to generate the signature.
YourServer.fetchOfferDetails(username: usernameHash, productIdentifier: productIdentifier, offerIdentifier: offerIdentifier, completion: { (nonce: UUID, timestamp: NSNumber, keyIdentifier: String, signature: String) in
// Create an SKPaymentDiscount to be used later when the user initiates the purchase
let discountOffer = SKPaymentDiscount(identifier: offerIdentifier, keyIdentifier: keyIdentifier, nonce: nonce, signature: discountsignature, timestamp: timestamp)
completion(discountOffer)
})
}
- 創(chuàng)建discount
// An example function that makes a buy request with a subscription offer attached.
public func buyProduct(productIdentifier: SKProduct, forUser usernameHash: String, withOffer discountOffer: SKPaymentDiscount) {
// The original product being purchased.
let payment = SKMutablePayment(product: product)
// You must set applicationUsername to be the same as the one used to generate the signature.
payment.applicationUsername = usernameHash
// Add the offer to the payment.
payment.paymentDiscount = discountOffer
// Add the payment to the queue for purchase.
SKPaymentQueue.default().add(payment)
}
如果sign生成錯(cuò)誤, 在支付的時(shí)候會(huì)出彈窗, 可以看看系統(tǒng)給你返回的什么錯(cuò)誤.

api
- 判斷當(dāng)前用戶是否可以享受優(yōu)惠策略
- 判斷當(dāng)前用戶享受那種優(yōu)惠策略(因?yàn)橐粋€(gè)商品可以有很多促銷id)
- 生成簽名
- 驗(yàn)證憑證和財(cái)務(wù)對(duì)賬
api在獲得憑證里面會(huì)看到一個(gè)字段promotional_offer_id, 這個(gè)字段就是你在ITunes connect 后臺(tái)創(chuàng)建促銷ID
| 字段名 | 定義 | 提供方 |
|---|---|---|
| appBundleID | 應(yīng)用的bundleId | api |
| keyIdentifier | 標(biāo)示用的是哪一個(gè)密鑰, 訂閱后臺(tái)可以拿到 | api |
| productIdentifier | 商品Id | 客戶端 |
| offerIdentifier | 促銷Id | api |
| applicationUsername | 傳用戶Id | 客戶端 |
| nonce | 有效期24小時(shí)的唯一Id,注意必須是小寫 | api |
| timestamp | 服務(wù)器時(shí)間戳,毫秒,時(shí)間戳24小時(shí)內(nèi)的優(yōu)惠是有效的 | api |
算法過程
- 先對(duì)以上的參數(shù)以字符串
\u2063作為分隔符按順序拼接:
appBundleId + '\u2063' + keyIdentifier + '\u2063' + productIdentifier + '\u2063' + offerIdentifier + '\u2063' + applicationUsername + '\u2063' + nonce + '\u2063' + timestamp
對(duì)拼接好的字符串做簽名:使用ECDSA算法,SHA-256哈希和iTunes Connect后臺(tái)生成的密鑰
-
對(duì)二進(jìn)制數(shù)據(jù)做base64,返回給App
本文末尾是簽名的python代碼
具體的獲取簽名的流程最好和貴司的安全風(fēng)控聊一下, 防止有安全問題, 比如優(yōu)惠券任意使用
遇到的問題
有很多坑蘋果文檔沒說清楚,也沒有谷歌到, 都是硬趟的. 也有很多不細(xì)心導(dǎo)致的
1. 如何設(shè)置和測(cè)試App Store推廣
測(cè)試的時(shí)候只能用URL Scheme測(cè)試, itms-services://?action=purchaseIntent&bundleId=com.example.app&productIdentifier=product_name 自己在測(cè)試的時(shí)候, 剛開始只跳轉(zhuǎn)App, 不回調(diào)方法, 最后查來查去, 發(fā)現(xiàn)自己的 URL Scheme里面多了一個(gè)空格, 醉了醉了
2. App Store 推廣的優(yōu)惠商品什么時(shí)候可以看到
我在商品上線之后,點(diǎn)擊了推廣, 在App Store的app的詳情頁(yè)的訂閱入口是過了半個(gè)多小時(shí)才出現(xiàn), 在App Store的檢索頁(yè)搜索APP, App下發(fā)的那個(gè)推廣是過了一晚上才出現(xiàn), 這個(gè)應(yīng)該是App Store的延遲導(dǎo)致的
3. 新上的App Store推廣商品老版本用戶在App Store看到如何處理
用戶點(diǎn)擊推廣商品, 還是會(huì)打開老版本App. 但是在App Store會(huì)彈出一個(gè)更新的頁(yè)面, 如下圖, 關(guān)鍵是這個(gè)頁(yè)面用戶看不到的, 我已經(jīng)從App Store跳轉(zhuǎn)到App了, 怎么可能看到這個(gè)界面, 而且跳轉(zhuǎn)到App Store之后是進(jìn)入主頁(yè)面打不開支付頁(yè)面的. 因?yàn)槟銢]實(shí)現(xiàn)蘋果的監(jiān)聽協(xié)議

4.確認(rèn)用戶是否享受推介促銷優(yōu)惠
這個(gè)權(quán)限完全是在蘋果那里控制, 蘋果根據(jù)Appid判斷, 客戶端是無法知曉的, 我司的做法是創(chuàng)建了兩個(gè)商品(比如愛奇藝,騰訊視頻),給從未購(gòu)買過促銷商品的用戶才下發(fā)帶有促銷訂閱優(yōu)惠的商品. 給享受過促銷優(yōu)惠的下發(fā)不帶促銷訂閱的商品, 當(dāng)然你們也可以只有一個(gè)商品(比如網(wǎng)易云音樂), 但是要防止用戶更換appid薅羊毛
5. 訂閱優(yōu)惠創(chuàng)建
這個(gè)是在創(chuàng)建簽名的時(shí)候發(fā)現(xiàn)我的開發(fā)者賬號(hào)沒辦法創(chuàng)建, 雖然leader給了我各種權(quán)限但是還是不行, 最后確認(rèn)是賬號(hào)持有人的權(quán)限才可以
6.切換訂閱
先看下蘋果定義
您可以將同一訂閱群組中的每個(gè) App 內(nèi)購(gòu)買項(xiàng)目都分配至一個(gè)訂閱等級(jí)。您的訂閱等級(jí)應(yīng)該按降序排列,從提供最高等級(jí)服務(wù)的那一個(gè)等級(jí)開始。如果訂閱提供的服務(wù)被視為等同的,則您可以向每個(gè)等級(jí)添加一個(gè)以上的訂閱。顧客可以在訂閱等級(jí)之間調(diào)整,即可以升級(jí)、降級(jí)和跨級(jí)。如果支付原價(jià)的訂閱者需要升級(jí)、降級(jí)或跨級(jí),則他們需要支付訂閱的當(dāng)前價(jià)格,而不是保留的原價(jià)。
升級(jí):當(dāng)顧客從較低等級(jí)的訂閱切換到較高等級(jí)的訂閱時(shí),顧客先前的 App 內(nèi)購(gòu)買項(xiàng)目金額將會(huì)按比例退還到原始付款方式。新的 App 內(nèi)購(gòu)買項(xiàng)目將收取完整價(jià)格并立即生效,顧客的續(xù)期日期也隨之更改為升級(jí)日期。
降級(jí):當(dāng)顧客從較高等級(jí)的訂閱切換到較低等級(jí)的訂閱時(shí),在下一個(gè)續(xù)期日期,會(huì)以新費(fèi)率向顧客收費(fèi)。
跨級(jí):當(dāng)顧客在相同等級(jí)的訂閱間進(jìn)行切換時(shí),如果 App 內(nèi)購(gòu)買項(xiàng)目的時(shí)限相同,那么顧客先前的 App 內(nèi)購(gòu)買項(xiàng)目金額將會(huì)按比例退還到原始付款方式。新的 App 內(nèi)購(gòu)買項(xiàng)目將收取完整價(jià)格并立即生效,顧客的續(xù)期日期也隨之更改為升級(jí)日期。如果 App 內(nèi)購(gòu)買項(xiàng)目的時(shí)限不同,那么跨級(jí)將會(huì)在顧客的下一個(gè)續(xù)期日期生效。
-------------我是分割線-----------------------
下面說一下我遇到的天坑
因?yàn)楫?dāng)初為了防止用戶薅羊毛(更換appid, 一直以促銷優(yōu)惠價(jià)格購(gòu)買), 所以就又新建了一個(gè)月的商品, 為了方便描述, 把原來的一個(gè)月vip稱為vipA, 新建的一個(gè)月vip稱為vipB.
因?yàn)槲覀僡pp一個(gè)群組的所有訂閱商品等級(jí)都是一樣的, 不涉及到升級(jí)和降級(jí). 只涉及到了跨級(jí). 用戶可以在App Store->管理訂閱->編輯訂閱中切換訂閱, 比如騰訊視頻, 他有五個(gè)一個(gè)月的商品(當(dāng)初我發(fā)現(xiàn)我們app的天坑之后我也去騰訊視頻薅過羊毛, 2333333)

重點(diǎn)來了!!!
由于vipA 和vip 都是相同時(shí)限, 相同價(jià)格, 假如用戶買完vipA, 蘋果下發(fā)了一個(gè)憑證, 客戶端透?jìng)鹘oAPI, API拿著這個(gè)憑證去蘋果校驗(yàn), 蘋果說這個(gè)憑證是真的, 然后API就給用戶下發(fā)了一個(gè)月會(huì)員期, 然后用戶馬上再次購(gòu)買vipB, 然后蘋果會(huì)把vipA的錢給退掉, 扣掉vipB的錢, 這個(gè)時(shí)候會(huì)生成一份新的支付憑證, API拿這個(gè)憑證去蘋果那里驗(yàn)證, 蘋果也說這個(gè)憑證是真的, 然后支付就給再次給這個(gè)用戶加了一個(gè)月的會(huì)員期限, 這樣用戶就有兩個(gè)月會(huì)員期了, 一個(gè)是vipA的一個(gè)是vipB的. 關(guān)鍵是蘋果已經(jīng)把vipA的錢給退給了用戶, 但是咱們給用戶下發(fā)了兩個(gè)月的會(huì)員期. 這就是被薅羊毛了. 當(dāng)初就是這個(gè)問題特意去蘋果中國(guó)那里去聊了一下, 最后和API溝通之后確認(rèn)了一下扣減會(huì)員的方案, 主要是根據(jù)server-to-server通知, 具體如下
- 區(qū)分CANCLE收據(jù)
(1).切換成檔位產(chǎn)生CANCLE通知: "notification_type":"CANCEL" "auto_renew_status":"true";
(2).取消訂閱且切換了檔位產(chǎn)生CANCLE通知: "notification_type":"CANCEL" "auto_renew_status":"false"
(3).正常的退款CANCEL通知:"notification_type":"CANCEL" "auto_renew_status":"false" "cancellation_date_ms":"1566487449000"
收到上面三種收據(jù)后根據(jù)
original_transaction_id拿到userID根據(jù)userID+CANCLE通知的
transaction_id可以直接查詢到具體哪一筆訂單根據(jù)CANCLE通知的時(shí)間和當(dāng)前訂單的
create_time創(chuàng)建時(shí)間判斷是否已經(jīng)超過周期了根據(jù)CANCLE通知的時(shí)間扣減會(huì)員
上述操作一定要留有操作記錄, 否則用戶發(fā)現(xiàn)自己會(huì)員期減少給客服投訴的時(shí)候要拿出完整的證據(jù)
7 transaction_id 不可靠
最近有個(gè)用戶反饋他更換設(shè)備之后, 客戶端告訴他會(huì)員扣費(fèi)成功, 然后他就來我們客服那里投訴. 投訴我們會(huì)員沒到期為什么扣費(fèi).
大體流程是, 用戶買的是訂閱會(huì)員, 10.1號(hào)到期了, 然后蘋果給客戶端下發(fā)了憑證, 也通過server-to-server告訴我們服務(wù)器有新的憑證了, 但是這兩個(gè)憑證是同一個(gè),transaction_id都一樣,所以給用戶下發(fā)了一個(gè)月的會(huì)員. 但是在10.17號(hào)用戶更新了設(shè)備, 導(dǎo)致蘋果給客戶端又下發(fā)了一份憑證,這個(gè)憑證的transaction_id和以前的不一樣, 數(shù)字比以前的加1了,而且蘋果也告知是合法憑證, 我們服務(wù)器覺得這是一個(gè)新的續(xù)費(fèi)憑證, 就給用戶加了一個(gè)月的會(huì)員, 然后發(fā)送通知告訴用戶續(xù)費(fèi)成功, 所以用戶對(duì)我們進(jìn)行了投訴. 其實(shí)是我們公司虧了, 白給用戶加了一個(gè)月的會(huì)員, 但是蘋果沒有給用戶扣費(fèi). 后來和蘋果的技術(shù)支持聊天, 告訴我們transaction_id不可靠, 最好還是用purchase-date-ms+bid+product-id來標(biāo)識(shí)這個(gè)憑證的唯一.
下面是蘋果的文檔關(guān)于expires-date的介紹, 里面也說了, 更換設(shè)備會(huì)更改transaction_id
https://developer.apple.com/documentation/appstorereceipts/transaction_id?language=objc
20191031更新
后來和蘋果的工程師溝通了一下, 用purchase-date-ms是毫秒, 所以這個(gè)值的后三位可能不可靠,建議用purchase-date比較, 聊的時(shí)候還說到了original-transaction-id, 這個(gè)字段在用戶切換在App Store國(guó)家的時(shí)候購(gòu)買也會(huì)變, 但是幾乎很少有用戶會(huì)觸發(fā)
這個(gè)是憑證中字段的解釋, 有問題還是看文檔
https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html
8 transactionDate 不可靠
20200116更新
最近后臺(tái)發(fā)現(xiàn)有很多客戶端上傳的transactionDate為空, 導(dǎo)致在鑒權(quán)的時(shí)候鑒權(quán)失敗, 我這邊查了下問題, 發(fā)現(xiàn)是蘋果在支付結(jié)果返回的方法- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions 的SKPaymentTransactionStatePurchased情況, 返回的對(duì)象SKPaymentTransaction里面的transactionDate為nil, 查了下蘋果文檔, 這個(gè)字段是將商品添加到server queue的時(shí)候的時(shí)間. 后來問了下服務(wù)端的同學(xué), 發(fā)現(xiàn)這個(gè)字段沒什么用, 只是判斷是否為空, 然后就把這個(gè)鑒權(quán)給去掉了.
是在搞不懂, 蘋果為啥有的時(shí)候把這個(gè)字段返回為nil, 坑
8 跨級(jí)憑證問題
20200120更新
今天偶然發(fā)現(xiàn)一個(gè)跨級(jí)的問題. 因?yàn)槲覀兊囊粋€(gè)訂閱群組里是有四個(gè)商品, 分別是, 訂閱周期一個(gè)月的A1,訂閱周期三個(gè)月的A3 訂閱周期六個(gè)月的A6, 訂閱周期12個(gè)月的A12. 之前我司的策略是只要用戶的會(huì)員在有效期內(nèi), 用戶在切換商品的時(shí)候都是不允許的. 包括用戶已經(jīng)在蘋果訂閱取消訂閱了, 但是還沒過會(huì)員期, 也是無法購(gòu)買的. 近期因?yàn)閍pp合規(guī)問題, 所以我們放開了這個(gè)策略, 改成了無論是否在會(huì)員期內(nèi), 用戶都可以購(gòu)買其他訂閱周期的商品.
現(xiàn)在我描述下用戶的行為, 用戶先買了一個(gè)A1, 然后用戶再次購(gòu)買A3商品, 因?yàn)锳1和A3是同一等級(jí), 但是有效期不一樣, 用戶的續(xù)費(fèi)會(huì)在A1結(jié)束后再以A3進(jìn)行續(xù)費(fèi). 用戶在購(gòu)買A3的時(shí)候返回的憑證里面的productID是A1商品的productID, 這里需要留意下
9 解析憑證新方法
以前解析蘋果的扣費(fèi)憑證都是這個(gè)憑證用base64工具解碼, 后來看到一種新操作, 用命令行, 在命令行執(zhí)行下面的代碼, 就可以拿到憑證的信息了, 其實(shí)就是給蘋果發(fā)請(qǐng)求驗(yàn)證憑證
正式環(huán)境的憑證
curl -i -H "Content-Type:application/json" -X POST -d '{"password":"秘鑰(找后臺(tái)要)","receipt-data":"憑證"}' https://buy.itunes.apple.com/verifyReceipt
沙箱環(huán)境的憑證
curl -i -H "Content-Type:application/json" -X POST -d '{"password":"秘鑰(找后臺(tái)要)","receipt-data":"憑證"}' https://sandbox.itunes.apple.com/verifyReceipt
20200304更新
有位大佬總結(jié)了獲取不到IPA產(chǎn)品列表的原因,列了13條, 感覺挺全的, 如果有問題建議看看這篇文章
https://inneka.com/programming/ios/reasons-for-skproductsrequest-returning-0-products/
20200326更新
蘋果在iOS13上把拉取商品請(qǐng)求的回調(diào)改成了異步線程回調(diào), 這個(gè)時(shí)候如果有ui上的操作的時(shí)候, 要回調(diào)到主線程, 否則會(huì)crash
iOS13.4+Xcode11, 必現(xiàn)crash
附錄
訂閱促銷簽名的python代碼 引自 鏈接
首先從iTunes Connect后臺(tái)下載.p8格式的密鑰,轉(zhuǎn)換成.cer,方便后續(xù)用python讀?。?/p>
openssl pkcs8 -nocrypt -in SubscriptionKey_xxxxxxxx.p8 -out cert.der -outform der
import json
import uuid
import time
import hashlib
import base64
from ecdsa import SigningKey
from ecdsa.util import sigencode_der
bundle_id = 'com.myapp'
key_id = 'XWSXTGQVX2'
product = 'com.myapp.product.a'
offer = 'REFERENCE_CODE' # This is the code set in ASC
application_username = 'user_name' # Should be the same you use when
# making purchases
nonce = uuid.uuid4()
timestamp = int(round(time.time() * 1000))
payload = '\u2063'.join([bundle_id,
key_id,
product,
offer,
application_username,
str(nonce), # Should be lower case
str(timestamp)])
# Read the key file
with open('cert.der', 'rb') as myfile:
der = myfile.read()
signing_key = SigningKey.from_der(der)
signature = signing_key.sign(payload.encode('utf-8'),
hashfunc=hashlib.sha256,
sigencode=sigencode_der)
encoded_signature = base64.b64encode(signature)
print(str(encoded_signature, 'utf-8'), str(nonce), str(timestamp), key_id)
有什么問題可以在下面討論, 希望可以幫助到你
我也寫了一篇WWDC2019關(guān)于內(nèi)購(gòu)新功能的文章