
Tutorial
September 8, 2022
70 min read
September 8, 2022
38 min read
pple đã ra mắt phiên bản StoreKit 2 mới tại WWDC 2021 diễn ra gần đây. Đây là khung quản lý mua hàng trong iOS. Tỷ lệ các ứng dụng có tính năng gói đăng ký và mua trong ứng dụng (in-app purchase) tăng trưởng ổn định và Apple đã đơn giản hóa việc tích hợp tính năng mua trong ứng dụng vào ứng dụng với việc phát hành StoreKit 2. Hôm nay, chúng ta sẽ tìm hiểu về làm việc với StoreKit 2 trên một phần của máy chủ, nói cách khác, với sự trợ giúp của App Store Server API.
Trong phiên bản API hiện tại, bạn cần Khóa bí mật chia sẻ (Shared Secret) để gửi yêu cầu. Khóa này là một chuỗi cố định bí mật mà bạn có thể nhận được trong App Store Connect. Phiên bản API mới sử dụng tiêu chuẩn JSON Web Token (JWT) để xác thực yêu cầu.
Đầu tiên, tạo một khóa riêng tư được sử dụng để cho phép các yêu cầu. Mở App Store Connect và đến phần Users and Access, sau đó đến tab Keys. Chọn loại khóa In-App Purchase. Tải xuống khóa mới. Bạn cũng sẽ cần ID của khóa đó – bạn có thể sao chép ID này trên cùng một trang với Issue ID trong tab App Store Connect API.
Bước tiếp theo là tạo một mã token được sử dụng để cho phép các yêu cầu. Quá trình này được mô tả chi tiết trong tài liệu, vì vậy bạn không cần bận tâm quá nhiều. Dưới đây là một ví dụ về việc triển khai có sẵn cho Python. Cần lưu ý rằng bạn không cần thiết tạo một mã token mới cho mọi yêu cầu mới. Khi tạo mã token, bạn đặt thời gian tồn tại của mã là tối đa 60 phút và sử dụng mã token đó trong khoảng thời gian này.
import time, uuid
from authlib.jose import jwt
BUNDLE_ID = 'com.adapty.sample_app'
ISSUER_ID = '4336a124-f214-4d40-883b-6db275b5e4aa'
KEY_ID = 'J65UYBDA74'
PRIVATE_KEY = '''
-----BEGIN PRIVATE KEY-----
MIGTAgMGByqGSMBHkAQQgR/fR+3Lkg4...
-----END PRIVATE KEY-----
'''
issue_time = round(time.time())
expiration_time = issue_time + 60 * 60 # 1 hour expiration
header = {
'alg': 'ES256',
'kid': KEY_ID,
'typ': 'JWT'
}
payload = {
'iss': ISSUER_ID,
'iat': issue_time,
'exp': expiration_time,
'aud': 'appstoreconnect-v1',
'nonce': str(uuid.uuid4()),
'bid': BUNDLE_ID
}
token_encoded = jwt.encode(header, payload, PRIVATE_KEY)
token_decoded = token_encoded.decode()
authorization_header = {
'Authorization': f'Bearer {token_decoded}'
}
Trong phiên bản API mới, tất cả các giao dịch được trả về theo tiêu chuẩn JSON Web Signature (JWS). Tiêu chuẩn này là một chuỗi bao gồm ba phần được phân cách bởi dấu chấm.
Base64(header) + "." + Base64(payload) + "." + sign(Base64(header) + "." + Base64(payload))
Cần có tiêu đề để đảm bảo giao dịch được xác thực. Khóa Alg chứa một thuật toán mã hóa, khóa x5c chứa một chuỗi chứng chỉ.
{
"kid": "AMP/DEV",
"alg": "ES256",
"x5c": [
"MIIEO...",
"MIIDK..."
]
}
{
"transactionId": "1000000831360853",
"originalTransactionId": "1000000806937552",
"webOrderLineItemId": "1000000063561721",
"bundleId": "com.adapty.sample_app",
"productId": "basic_subscription_1_month",
"subscriptionGroupIdentifier": "27636320",
"purchaseDate": 1624446341000,
"originalPurchaseDate": 1619686337000,
"expiresDate": 1624446641000,
"quantity": 1,
"type": "Auto-Renewable Subscription",
"appAccountToken": "fd12746f-2d3a-46c8-bff8-55b75ed06aca",
"inAppOwnershipType": "PURCHASED",
"signedDate": 1624446484882,
"offerType": 2,
"offerIdentifier": "basic_subscription_1_month.pay_as_you_go.3_months"
}
Apple đã thay đổi và mở rộng định dạng giao dịch. Theo tôi thì bây giờ, làm việc với các định dạng đó sẽ thuận tiện hơn. Bạn có thể tìm hiểu chi tiết về một định dạng mới trong tài liệu. Sau đây, tôi sẽ mô tả những thay đổi quan trọng nhất.
Nếu ưu đãi khuyến mãi hoặc mã ưu đãi đã được sử dụng, khóa offerIdentifier sẽ chứa ID của ưu đãi đã sử dụng. Trước đây, không thể theo dõi việc sử dụng ưu đãi ở phía máy chủ, điều này khiến số liệu phân tích xấu đi. Bây giờ, bạn có thể sử dụng mã ưu đãi để phân tích.
Để kiểm tra trạng thái gói đăng ký của người dùng hiện tại, gửi một yêu cầu GET tới https://api.storekit.itunes.apple.com/inApps/v1/subscriptions/ {originalTransactionId}, trong đó {originalTransactionId} là ID của bất kỳ chuỗi giao dịch nào của người dùng. Đổi lại, bạn sẽ nhận được các giao dịch với trạng thái cho mọi nhóm đăng ký.
{
"environment": "Sandbox",
"bundleId": "com.adapty.sample_app",
"data": [
{
"subscriptionGroupIdentifier": "39636320",
"lastTransactions": [
{
"originalTransactionId": "1000000819078552",
"status": 2,
"signedTransactionInfo": "eyJraWQiOi...",
"signedRenewalInfo": "eyJraWQiOi..."
}
]
}
]
}
Khóa tình trạng hiển thị trạng thái gói đăng ký hiện tại, dựa vào đó, bạn có thể quyết định xem mình có nên cung cấp cho người dùng quyền truy cập vào các tính năng trả phí của ứng dụng hay không. Những giá trị khả thi:
Khóa SignedTransactionInfo chứa thông tin về giao dịch cuối cùng trong chuỗi. Bạn có thể tìm thấy chi tiết về định dạng của khóa này ở trên.
Khóa SignedRenewalInfo chứa thông tin gia hạn gói đăng ký.
{
"expirationIntent": 1,
"originalTransactionId": "1000000819078552",
"autoRenewProductId": "basic_subscription_1_month",
"productId": "basic_subscription_1_month",
"autoRenewStatus": 0,
"isInBillingRetryPeriod": false,
"signedDate": 1624520884048
}
Thông tin này cho phép chúng tôi biết được điều sẽ xảy ra với gói đăng ký trong kỳ thanh toán tiếp theo. Ví dụ: nếu bạn thấy người dùng đã hủy tự động gia hạn, bạn có thể đề nghị họ chuyển sang gói đăng ký khác hoặc cung cấp cho họ một ưu đãi khuyến mãi. Thật thuận tiện khi theo dõi loại sự kiện này với sự trợ giúp của thông báo máy chủ (server notifications) mà tôi sẽ cho bạn biết ngay sau đây.
Để lấy lịch sử giao dịch của người dùng, gửi yêu cầu GET tới https://api.storekit.itunes.apple.com/inApps/v1/history/{originalTransactionId}, trong đó {originalTransactionId} là ID của bất kỳ chuỗi giao dịch nào của người dùng. Đổi lại, bạn sẽ nhận được một loạt các giao dịch được sắp xếp theo thời gian.
{
"revision": "1625872984000_1000000212854038",
"bundleId": "com.adapty.sample_app",
"environment": "Sandbox",
"hasMore": true,
"signedTransactions": [
"eyJraWQiOiJ...",
"joiRVMyNeyX...",
"5MnkvOTlOZl...",
...
]
}
Một yêu cầu không được chứa nhiều hơn 20 giao dịch. Nếu người dùng có số lượng giao dịch lớn hơn, giá trị của cờ hasMore sẽ là true. Nếu bạn cần trang giao dịch tiếp theo, hãy gửi lại yêu cầu có chứa thông số GET sửa đổi. Nó sẽ chứa giá trị từ cùng một khóa.
Thông báo máy chủ giúp nhận thông tin về các giao dịch mua, gia hạn, các vấn đề về thanh toán mới, v.v. Điều này giúp xây dựng số liệu phân tích chính xác hơn, cũng như quản lý trạng thái của người đăng ký dễ dàng hơn.
Các thông báo máy chủ hiện có (V1) có thể giải quyết hầu hết các vấn đề, nhưng đôi khi các thông báo này khá bất tiện. Hầu hết, đó là về tình huống khi bạn nhận được nhiều thông báo chỉ cho một hành động của người dùng. Ví dụ: bây giờ, khi người dùng nâng cấp gói đăng ký, Apple sẽ gửi hai thông báo: DID_CHANGE_RENEWAL_STATUS và INTERACTIVE_RENEWAL. Để xử lý trường hợp này, bạn hiện cần lưu trạng thái bằng cách nào đó và kiểm tra xem thông báo thứ hai đã được gửi chưa. Trong phiên bản thông báo máy chủ mới (V2), chỉ có một thông báo cho một hành động của người dùng. Cách này thuận tiện hơn nhiều.
Phiên bản thông báo máy chủ thứ hai có các sự kiện mới – OFFER_REDEEMED, EXPIRED và GRACE_PERIOD_EXPIRED. Các sự kiện này giúp quản lý trạng thái người đăng ký dễ dàng hơn nhiều. Sự kiện SUBSCRIBED và PRICE_INCREASE là những sự kiện được cải tiến từ phiên bản đầu tiên.
Thông báo hiện có nhiều loại, do đó, một thông báo cho bất kỳ hành động nào của người dùng là đủ để hiểu chuyện xảy ra.
Các loại thông báo
{
"notificationType": "SUBSCRIBED",
"subtype": "INITIAL_BUY",
"version": 2,
"data": {
"environment": "Sandbox",
"bundleId": "com.adapty.sample_app",
"appAppleId": 739104078,
"bundleVersion": 1,
"signedTransactionInfo": "eyJraWQiOi...",
"signedRenewalInfo": "eyJraWQiOi..."
}
}
Thông báo máy chủ chứa thông tin về một giao dịch và một lần gia hạn ở định dạng JWS mà tôi đã mô tả trước đó.
Bạn cần sử dụng URL của môi trường Sandbox để kiểm tra các giao dịch mua: https://api.storekit-sandbox.itunes.apple.com.
Hiện chưa có phiên bản mới của thông báo máy chủ để kiểm tra. Khi có phiên bản mới, bạn có thể chỉ định các URL khác nhau cho các thông báo Production và Sandbox. Bạn có thể chọn V2 cho Sandbox và V1 cho Production để kiểm tra.
Ngoài ra, App Store Connect hiện cho phép:
Apple đã cải thiện đáng kể cách làm việc với các gói đăng ký và mua trong ứng dụng ở phía máy chủ. Theo tôi thì đây là các tính năng mới hữu ích nhất:
Việc chuyển sang một API mới sẽ không khó, bạn chỉ cần nhận được originalTransactionId cho mỗi biên nhận là đủ. Có thể biên nhận đã có trong cơ sở của bạn.
Dù sao, phần khó nhất của việc tích hợp gói đăng ký vào ứng dụng di động là xây dựng hệ thống phân tích và tối ưu hóa nền kinh tế. Adapty có thể giải quyết tốt những vấn đề này:
Tìm hiểu thêm về các tính năng này, để triển khai gói đăng ký vào ứng dụng của bạn nhanh hơn và kiếm tiền từ ứng dụng sớm hơn và hiệu quả hơn.
Further reading
Tutorial
September 8, 2022
70 min read
Tutorial
September 8, 2022
54 min read