Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DO NOT MERGE Add metadata to purchase #4293

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Sources/Networking/Backend.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,14 @@ class Backend {
transactionData: PurchasedTransactionData,
observerMode: Bool,
appTransaction: String? = nil,
transactionMetadata: [String: String]? = nil,
completion: @escaping CustomerAPI.CustomerInfoResponseHandler) {
self.customer.post(receipt: receipt,
productData: productData,
transactionData: transactionData,
observerMode: observerMode,
appTransaction: appTransaction,
transactionMetadata: transactionMetadata,
completion: completion)
}

Expand Down
1 change: 1 addition & 0 deletions Sources/Networking/CustomerAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ final class CustomerAPI {
transactionData: PurchasedTransactionData,
observerMode: Bool,
appTransaction: String?,
transactionMetadata: [String: String]?,
completion: @escaping CustomerAPI.CustomerInfoResponseHandler) {
var subscriberAttributesToPost: SubscriberAttribute.Dictionary?

Expand Down
7 changes: 6 additions & 1 deletion Sources/Networking/Operations/PostReceiptDataOperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ extension PostReceiptDataOperation {
/// The [AppTransaction](https://developer.apple.com/documentation/storekit/apptransaction) JWS token
/// retrieved from StoreKit 2.
let appTransaction: String?

let transactionMetadata: [String: String]?
}

struct Paywall {
Expand Down Expand Up @@ -185,7 +187,8 @@ extension PostReceiptDataOperation.PostData {
subscriberAttributesByKey: data.unsyncedAttributes,
aadAttributionToken: data.aadAttributionToken,
testReceiptIdentifier: testReceiptIdentifier,
appTransaction: appTransaction
appTransaction: appTransaction,
transactionMetadata: data.transactionMetadata
)
}

Expand Down Expand Up @@ -268,6 +271,7 @@ extension PostReceiptDataOperation.PostData: Encodable {
case paywall
case testReceiptIdentifier = "test_receipt_identifier"
case appTransaction = "app_transaction"
case transactionMetadata = "metadata"

}

Expand All @@ -285,6 +289,7 @@ extension PostReceiptDataOperation.PostData: Encodable {

try container.encodeIfPresent(self.fetchToken, forKey: .fetchToken)
try container.encodeIfPresent(self.appTransaction, forKey: .appTransaction)
try container.encodeIfPresent(self.transactionMetadata, forKey: .transactionMetadata)
try container.encodeIfPresent(self.presentedOfferingIdentifier, forKey: .presentedOfferingIdentifier)
try container.encodeIfPresent(self.presentedPlacementIdentifier, forKey: .presentedPlacementIdentifier)
try container.encodeIfPresent(self.appliedTargetingRule, forKey: .appliedTargetingRule)
Expand Down
28 changes: 18 additions & 10 deletions Sources/Purchasing/Purchases/Purchases.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,22 @@ public typealias StartPurchaseBlock = (@escaping PurchaseCompletedBlock) -> Void
* framework handle the singleton instance for you.
*/
@objc(RCPurchases) public final class Purchases: NSObject, PurchasesType, PurchasesSwiftType {
public func purchase(product: StoreProduct, completion: @escaping PurchaseCompletedBlock) {
purchasesOrchestrator.purchase(product: product, package: nil, transactionMetadata: nil, completion: completion)
}

public func purchase(package: Package, completion: @escaping PurchaseCompletedBlock) {
purchasesOrchestrator.purchase(product: package.storeProduct, package: nil, transactionMetadata: nil, completion: completion)
}

public func purchase(product: StoreProduct, transactionMetadata: [String : String]?, completion: @escaping PurchaseCompletedBlock) {
purchasesOrchestrator.purchase(product: product, package: nil, transactionMetadata: transactionMetadata, completion: completion)
}

public func purchase(package: Package, transactionMetadata: [String : String]?, completion: @escaping PurchaseCompletedBlock) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of making brand new purchase functions to make a purchase with metadata, I'd consider modifying the existing purchase functions and making the transactionMetadata parameter default to nil so that it isn't a breaking change.

There are quite a few purchase functions today across both Purchases.swift and Purchases+async.swift. Ideally folks would be able to upload transaction metadata no matter which function call they're already using without having to migrate to a new one.

purchasesOrchestrator.purchase(product: package.storeProduct, package: package, transactionMetadata: transactionMetadata, completion: completion)
}


/// Returns the already configured instance of ``Purchases``.
/// - Warning: this method will crash with `fatalError` if ``Purchases`` has not been initialized through
Expand Down Expand Up @@ -949,20 +965,10 @@ public extension Purchases {
return await productsAsync(productIdentifiers)
}

@objc(purchaseProduct:withCompletion:)
func purchase(product: StoreProduct, completion: @escaping PurchaseCompletedBlock) {
purchasesOrchestrator.purchase(product: product, package: nil, completion: completion)
}

func purchase(product: StoreProduct) async throws -> PurchaseResultData {
return try await purchaseAsync(product: product)
}

@objc(purchasePackage:withCompletion:)
func purchase(package: Package, completion: @escaping PurchaseCompletedBlock) {
purchasesOrchestrator.purchase(product: package.storeProduct, package: package, completion: completion)
}

func purchase(package: Package) async throws -> PurchaseResultData {
return try await purchaseAsync(package: package)
}
Expand Down Expand Up @@ -1000,6 +1006,7 @@ public extension Purchases {
purchasesOrchestrator.purchase(product: product,
package: nil,
promotionalOffer: promotionalOffer.signedData,
transactionMetadata: nil,
completion: completion)
}

Expand All @@ -1012,6 +1019,7 @@ public extension Purchases {
purchasesOrchestrator.purchase(product: package.storeProduct,
package: package,
promotionalOffer: promotionalOffer.signedData,
transactionMetadata: nil,
completion: completion)
}

Expand Down
23 changes: 17 additions & 6 deletions Sources/Purchasing/Purchases/PurchasesOrchestrator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ final class PurchasesOrchestrator {

func purchase(product: StoreProduct,
package: Package?,
transactionMetadata: [String: String]?,
completion: @escaping PurchaseCompletedBlock) {
Self.logPurchase(product: product, package: package)

Expand All @@ -325,6 +326,7 @@ final class PurchasesOrchestrator {
self.purchase(sk2Product: sk2Product,
package: package,
promotionalOffer: nil,
transactionMetadata: transactionMetadata,
completion: completion)
} else if product.isTestProduct {
self.handleTestProduct(completion)
Expand All @@ -336,6 +338,7 @@ final class PurchasesOrchestrator {
func purchase(product: StoreProduct,
package: Package?,
promotionalOffer: PromotionalOffer.SignedData,
transactionMetadata: [String: String]?,
completion: @escaping PurchaseCompletedBlock) {
Self.logPurchase(product: product, package: package, offer: promotionalOffer)

Expand All @@ -352,6 +355,7 @@ final class PurchasesOrchestrator {
self.purchase(sk2Product: sk2Product,
package: package,
promotionalOffer: promotionalOffer,
transactionMetadata: transactionMetadata,
completion: completion)
} else if product.isTestProduct {
self.handleTestProduct(completion)
Expand Down Expand Up @@ -440,12 +444,14 @@ final class PurchasesOrchestrator {
func purchase(sk2Product product: SK2Product,
package: Package?,
promotionalOffer: PromotionalOffer.SignedData?,
transactionMetadata: [String: String]?,
completion: @escaping PurchaseCompletedBlock) {
_ = Task<Void, Never> {
do {
let result: PurchaseResultData = try await self.purchase(sk2Product: product,
package: package,
promotionalOffer: promotionalOffer)
promotionalOffer: promotionalOffer,
transactionMetadata: transactionMetadata)

if !result.userCancelled {
Logger.rcPurchaseSuccess(Strings.purchase.purchased_product(
Expand Down Expand Up @@ -479,7 +485,8 @@ final class PurchasesOrchestrator {
func purchase(
sk2Product: SK2Product,
package: Package?,
promotionalOffer: PromotionalOffer.SignedData?
promotionalOffer: PromotionalOffer.SignedData?,
transactionMetadata: [String: String]?
) async throws -> PurchaseResultData {
let result: Product.PurchaseResult

Expand Down Expand Up @@ -535,7 +542,7 @@ final class PurchasesOrchestrator {
let customerInfo: CustomerInfo

if let transaction = transaction {
customerInfo = try await self.handlePurchasedTransaction(transaction, .purchase)
customerInfo = try await self.handlePurchasedTransaction(transaction, .purchase, transactionMetadata)
} else {
// `transaction` would be `nil` for `Product.PurchaseResult.pending` and
// `Product.PurchaseResult.userCancelled`.
Expand Down Expand Up @@ -779,14 +786,16 @@ extension PurchasesOrchestrator: PaymentQueueWrapperDelegate {
startPurchase = { completion in
self.purchase(product: product,
package: nil,
promotionalOffer: discount) { transaction, customerInfo, error, cancelled in
promotionalOffer: discount,
transactionMetadata: nil) { transaction, customerInfo, error, cancelled in
completion(transaction, customerInfo, error, cancelled)
}
}
} else {
startPurchase = { completion in
self.purchase(product: product,
package: nil) { transaction, customerInfo, error, cancelled in
package: nil,
transactionMetadata: nil) { transaction, customerInfo, error, cancelled in
completion(transaction, customerInfo, error, cancelled)
}
}
Expand Down Expand Up @@ -1481,7 +1490,8 @@ extension PurchasesOrchestrator {

private func handlePurchasedTransaction(
_ transaction: StoreTransaction,
_ initiationSource: ProductRequestData.InitiationSource
_ initiationSource: ProductRequestData.InitiationSource,
_ transactionMetadata: [String: String]?
) async throws -> CustomerInfo {
let storefront = await Storefront.currentStorefront
let offeringContext = self.getAndRemovePresentedOfferingContext(for: transaction)
Expand All @@ -1493,6 +1503,7 @@ extension PurchasesOrchestrator {
presentedOfferingContext: offeringContext,
presentedPaywall: paywall,
unsyncedAttributes: unsyncedAttributes,
transactionMetadata: transactionMetadata,
aadAttributionToken: adServicesToken,
storefront: storefront,
source: .init(isRestore: self.allowSharingAppStoreAccount,
Expand Down
6 changes: 5 additions & 1 deletion Sources/Purchasing/Purchases/TransactionPoster.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ struct PurchasedTransactionData {
var presentedOfferingContext: PresentedOfferingContext?
var presentedPaywall: PaywallEvent?
var unsyncedAttributes: SubscriberAttribute.Dictionary?
var transactionMetadata: [String: String]?
var aadAttributionToken: String?
var storefront: StorefrontType?
var source: PurchaseSource
Expand Down Expand Up @@ -112,6 +113,7 @@ final class TransactionPoster: TransactionPosterType {
receipt: encodedReceipt,
product: product,
appTransaction: appTransaction,
transactionMetadata: data.transactionMetadata,
completion: completion)
}
}
Expand Down Expand Up @@ -240,14 +242,16 @@ private extension TransactionPoster {
receipt: EncodedAppleReceipt,
product: StoreProduct?,
appTransaction: String?,
transactionMetadata: [String: String]?,
completion: @escaping CustomerAPI.CustomerInfoResponseHandler) {
let productData = product.map { ProductRequestData(with: $0, storefront: purchasedTransactionData.storefront) }

self.backend.post(receipt: receipt,
productData: productData,
transactionData: purchasedTransactionData,
observerMode: self.observerMode,
appTransaction: appTransaction) { result in
appTransaction: appTransaction,
transactionMetadata: transactionMetadata) { result in
self.handleReceiptPost(withTransaction: transaction,
result: result.map { ($0, product) },
subscriberAttributes: purchasedTransactionData.unsyncedAttributes,
Expand Down