Anonymous
Как я справляюсь с ожидающими транзакциями
Сообщение
Anonymous » 15 мар 2025, 18:15
Вот как я справляюсь с ожидающими транзакциями в моем приложении < /p>
Код: Выделить всё
import StoreKit
import AmplitudeSwift
import Optimizely
class PurchaseManager: ObservableObject {
// A published property to hold available products
@Published var products: [Product] = []
// A published property to track the status of transactions
@Published var transactionState: String = "Idle"
var loadingIndicator: ThreeBubblesLoadingView!
// A set of product identifiers
private let productIdentifiers: Set = [
PaymentHandler.sharedInstance.YEARLY_PRODUCT_ID,
PaymentHandler.sharedInstance.YEARLY_PRODUCT_ID_50_OFF,
PaymentHandler.sharedInstance.MONTHLY_PRODUCT_ID,
PaymentHandler.sharedInstance.YEARLY_PRODUCT_ID_40_OFF,
PaymentHandler.sharedInstance.YEARLY_PRODUCT_ID_FREE_TRIAL,
PaymentHandler.sharedInstance.YEARLY_PRODUCT_ID_50,
PaymentHandler.sharedInstance.MONTHLY_PRODUCT_ID_13
]
// Shared instance to be used throughout the app
static let shared = PurchaseManager()
private init() {}
// MARK: - Fetch Products from App Store
func fetchProducts() async {
do {
let products = try await Product.products(for: productIdentifiers)
self.products = products
} catch {
print("Failed to fetch products: \(error.localizedDescription)")
}
}
// MARK: - Handle Purchase
func purchaseProduct(product: Product, source: String, vc: UIViewController) async -> Bool {
do {
DispatchQueue.main.async {
self.loadingIndicator = ThreeBubblesLoadingView()
self.loadingIndicator.translatesAutoresizingMaskIntoConstraints = false
vc.view.addSubview(self.loadingIndicator)
NSLayoutConstraint.activate([
self.loadingIndicator.centerXAnchor.constraint(equalTo: vc.view.centerXAnchor),
self.loadingIndicator.centerYAnchor.constraint(equalTo: vc.view.centerYAnchor)
])
}
// Start the purchase
let result = try await product.purchase()
// Handle the result of the purchase
switch result {
case .success(let verificationResult):
switch verificationResult {
case .verified(let transaction):
self.transactionState = "Purchase Successful"
await transaction.finish()
DispatchQueue.main.async {
Amplitude.sharedInstance.track(
eventType: "payment_completed",
eventProperties: [
"PlanId": transaction.productID,
"UserId": WUser.sharedInstance.userId,
"Source": source,
"VariationKey": WUser.sharedInstance.variationKey
]
)
if (self.loadingIndicator != nil) {
self.loadingIndicator.removeFromSuperview()
}
}
return await PaymentHandler.sharedInstance.purchase(
vc: vc,
productId: transaction.productID,
product: transaction.productID,
transaction: transaction
)
case .unverified(let transaction, let error):
self.transactionState = "Purchase Unverified: \(error.localizedDescription)"
await transaction.finish()
DispatchQueue.main.async {
showMessageWithTitle("Error!", "There was an error processing your purchase", .error)
Amplitude.sharedInstance.track(
eventType: "payment_failed",
eventProperties: [
"PlanId": transaction.productID,
"UserId": WUser.sharedInstance.userId,
"Source": source,
"Error": error.localizedDescription,
"ErrorType": "UnverifiedTransaction",
"ErrorObject": String(describing: error)
]
)
if (self.loadingIndicator != nil) {
self.loadingIndicator.removeFromSuperview()
}
}
return false
}
case .userCancelled:
self.transactionState = "User cancelled the purchase."
DispatchQueue.main.async {
Amplitude.sharedInstance.track(
eventType: "payment_cancelled",
eventProperties: [
"PlanId": product.id,
"UserId": WUser.sharedInstance.userId,
"Source": source
]
)
if (self.loadingIndicator != nil) {
self.loadingIndicator.removeFromSuperview()
}
}
return false
case .pending:
self.transactionState = "Purchase is pending."
DispatchQueue.main.async {
Amplitude.sharedInstance.track(
eventType: "payment_pending",
eventProperties: [
"PlanId": product.id,
"UserId": WUser.sharedInstance.userId,
"Source": source
]
)
if (self.loadingIndicator != nil) {
self.loadingIndicator.removeFromSuperview()
}
}
return false
@unknown default:
self.transactionState = "Unknown purchase result."
DispatchQueue.main.async {
showMessageWithTitle("Error!", "There was an error processing your purchase", .error)
Amplitude.sharedInstance.track(
eventType: "payment_failed",
eventProperties: [
"PlanId": product.id,
"UserId": WUser.sharedInstance.userId,
"Source": source,
"Error": "unknown"
]
)
if (self.loadingIndicator != nil) {
self.loadingIndicator.removeFromSuperview()
}
}
return false
}
} catch {
self.transactionState = "Purchase failed: \(error.localizedDescription)"
DispatchQueue.main.async {
showMessageWithTitle("Error!", "There was an error processing your purchase", .error)
Amplitude.sharedInstance.track(
eventType: "payment_failed",
eventProperties: [
"PlanId": product.id,
"UserId": WUser.sharedInstance.userId,
"Source": source,
"Error": error.localizedDescription,
"ErrorType": "CatchError",
"ErrorObject": String(describing: error)
]
)
self.loadingIndicator.removeFromSuperview()
}
return false
}
}
// MARK: - Listen for Transaction Updates
func listenForTransactionUpdates() {
Task {
for await result in Transaction.updates {
switch result {
case .verified(let transaction):
self.transactionState = "Transaction verified: \(transaction.productID)"
await transaction.finish()
DispatchQueue.main.async {
Amplitude.sharedInstance.track(
eventType: "payment_completed",
eventProperties: [
"PlanId": transaction.productID,
"UserId": WUser.sharedInstance.userId,
"TransactionType": "Pending"
]
)
if (self.loadingIndicator != nil) {
self.loadingIndicator.removeFromSuperview()
}
}
if (PaymentHandler.sharedInstance.vc != nil) {
await PaymentHandler.sharedInstance.purchase(
vc: PaymentHandler.sharedInstance.vc!,
productId: transaction.productID,
product: transaction.productID,
transaction: transaction
)
}
case .unverified(let transaction, let error):
self.transactionState = "Unverified transaction: \(error.localizedDescription)"
DispatchQueue.main.async {
Amplitude.sharedInstance.track(
eventType: "payment_failed",
eventProperties: [
"PlanId": transaction.productID,
"UserId": WUser.sharedInstance.userId,
"Error": error.localizedDescription,
"ErrorType": "UnverifiedPendingTransaction",
"ErrorObject": String(describing: error)
]
)
if (self.loadingIndicator != nil) {
self.loadingIndicator.removeFromSuperview()
}
}
await transaction.finish()
}
}
}
}
}
< /code>
Когда я совершаю покупку, я вызываю функцию Boickeseproduct. < /p>
К сожалению, ожидающая транзакция не обрабатывается. Кто -нибудь может помочь? Около 5 транзакций прошли как ожидающие, но не были обработаны Apple. Оплата не была захвачена. Этот код неправильно?PurchaseManager.shared.listenForTransactionUpdates()
Плата не отображается в подключении App Store, и я не получаю уведомление Apple Server об этом.
Подробнее здесь:
https://stackoverflow.com/questions/795 ... ansactions
1742051743
Anonymous
Вот как я справляюсь с ожидающими транзакциями в моем приложении < /p> [code]import StoreKit import AmplitudeSwift import Optimizely class PurchaseManager: ObservableObject { // A published property to hold available products @Published var products: [Product] = [] // A published property to track the status of transactions @Published var transactionState: String = "Idle" var loadingIndicator: ThreeBubblesLoadingView! // A set of product identifiers private let productIdentifiers: Set = [ PaymentHandler.sharedInstance.YEARLY_PRODUCT_ID, PaymentHandler.sharedInstance.YEARLY_PRODUCT_ID_50_OFF, PaymentHandler.sharedInstance.MONTHLY_PRODUCT_ID, PaymentHandler.sharedInstance.YEARLY_PRODUCT_ID_40_OFF, PaymentHandler.sharedInstance.YEARLY_PRODUCT_ID_FREE_TRIAL, PaymentHandler.sharedInstance.YEARLY_PRODUCT_ID_50, PaymentHandler.sharedInstance.MONTHLY_PRODUCT_ID_13 ] // Shared instance to be used throughout the app static let shared = PurchaseManager() private init() {} // MARK: - Fetch Products from App Store func fetchProducts() async { do { let products = try await Product.products(for: productIdentifiers) self.products = products } catch { print("Failed to fetch products: \(error.localizedDescription)") } } // MARK: - Handle Purchase func purchaseProduct(product: Product, source: String, vc: UIViewController) async -> Bool { do { DispatchQueue.main.async { self.loadingIndicator = ThreeBubblesLoadingView() self.loadingIndicator.translatesAutoresizingMaskIntoConstraints = false vc.view.addSubview(self.loadingIndicator) NSLayoutConstraint.activate([ self.loadingIndicator.centerXAnchor.constraint(equalTo: vc.view.centerXAnchor), self.loadingIndicator.centerYAnchor.constraint(equalTo: vc.view.centerYAnchor) ]) } // Start the purchase let result = try await product.purchase() // Handle the result of the purchase switch result { case .success(let verificationResult): switch verificationResult { case .verified(let transaction): self.transactionState = "Purchase Successful" await transaction.finish() DispatchQueue.main.async { Amplitude.sharedInstance.track( eventType: "payment_completed", eventProperties: [ "PlanId": transaction.productID, "UserId": WUser.sharedInstance.userId, "Source": source, "VariationKey": WUser.sharedInstance.variationKey ] ) if (self.loadingIndicator != nil) { self.loadingIndicator.removeFromSuperview() } } return await PaymentHandler.sharedInstance.purchase( vc: vc, productId: transaction.productID, product: transaction.productID, transaction: transaction ) case .unverified(let transaction, let error): self.transactionState = "Purchase Unverified: \(error.localizedDescription)" await transaction.finish() DispatchQueue.main.async { showMessageWithTitle("Error!", "There was an error processing your purchase", .error) Amplitude.sharedInstance.track( eventType: "payment_failed", eventProperties: [ "PlanId": transaction.productID, "UserId": WUser.sharedInstance.userId, "Source": source, "Error": error.localizedDescription, "ErrorType": "UnverifiedTransaction", "ErrorObject": String(describing: error) ] ) if (self.loadingIndicator != nil) { self.loadingIndicator.removeFromSuperview() } } return false } case .userCancelled: self.transactionState = "User cancelled the purchase." DispatchQueue.main.async { Amplitude.sharedInstance.track( eventType: "payment_cancelled", eventProperties: [ "PlanId": product.id, "UserId": WUser.sharedInstance.userId, "Source": source ] ) if (self.loadingIndicator != nil) { self.loadingIndicator.removeFromSuperview() } } return false case .pending: self.transactionState = "Purchase is pending." DispatchQueue.main.async { Amplitude.sharedInstance.track( eventType: "payment_pending", eventProperties: [ "PlanId": product.id, "UserId": WUser.sharedInstance.userId, "Source": source ] ) if (self.loadingIndicator != nil) { self.loadingIndicator.removeFromSuperview() } } return false @unknown default: self.transactionState = "Unknown purchase result." DispatchQueue.main.async { showMessageWithTitle("Error!", "There was an error processing your purchase", .error) Amplitude.sharedInstance.track( eventType: "payment_failed", eventProperties: [ "PlanId": product.id, "UserId": WUser.sharedInstance.userId, "Source": source, "Error": "unknown" ] ) if (self.loadingIndicator != nil) { self.loadingIndicator.removeFromSuperview() } } return false } } catch { self.transactionState = "Purchase failed: \(error.localizedDescription)" DispatchQueue.main.async { showMessageWithTitle("Error!", "There was an error processing your purchase", .error) Amplitude.sharedInstance.track( eventType: "payment_failed", eventProperties: [ "PlanId": product.id, "UserId": WUser.sharedInstance.userId, "Source": source, "Error": error.localizedDescription, "ErrorType": "CatchError", "ErrorObject": String(describing: error) ] ) self.loadingIndicator.removeFromSuperview() } return false } } // MARK: - Listen for Transaction Updates func listenForTransactionUpdates() { Task { for await result in Transaction.updates { switch result { case .verified(let transaction): self.transactionState = "Transaction verified: \(transaction.productID)" await transaction.finish() DispatchQueue.main.async { Amplitude.sharedInstance.track( eventType: "payment_completed", eventProperties: [ "PlanId": transaction.productID, "UserId": WUser.sharedInstance.userId, "TransactionType": "Pending" ] ) if (self.loadingIndicator != nil) { self.loadingIndicator.removeFromSuperview() } } if (PaymentHandler.sharedInstance.vc != nil) { await PaymentHandler.sharedInstance.purchase( vc: PaymentHandler.sharedInstance.vc!, productId: transaction.productID, product: transaction.productID, transaction: transaction ) } case .unverified(let transaction, let error): self.transactionState = "Unverified transaction: \(error.localizedDescription)" DispatchQueue.main.async { Amplitude.sharedInstance.track( eventType: "payment_failed", eventProperties: [ "PlanId": transaction.productID, "UserId": WUser.sharedInstance.userId, "Error": error.localizedDescription, "ErrorType": "UnverifiedPendingTransaction", "ErrorObject": String(describing: error) ] ) if (self.loadingIndicator != nil) { self.loadingIndicator.removeFromSuperview() } } await transaction.finish() } } } } } < /code> Когда я совершаю покупку, я вызываю функцию Boickeseproduct. < /p> К сожалению, ожидающая транзакция не обрабатывается. Кто -нибудь может помочь? Около 5 транзакций прошли как ожидающие, но не были обработаны Apple. Оплата не была захвачена. Этот код неправильно?PurchaseManager.shared.listenForTransactionUpdates() [/code] Плата не отображается в подключении App Store, и я не получаю уведомление Apple Server об этом. Подробнее здесь: [url]https://stackoverflow.com/questions/79511123/how-i-handle-pending-transactions[/url]