pilotman
pilotman

Reputation: 675

iOS: How to get transaction id or receipt data after successful in app purchase Swift

I am wondering how after a successful purchase can I get the transaction id or receipt information so that i can then send to my server and use for purchase validation. Also on that same topic I am wondering that if for some reason the connection is interrupted before I send the purchase information to my server then how would I go about linking the notifications to the user that purchased the subscriptions?

Thanks

So I want to do my processing in the .purchased part of the switch below

import Foundation
import StoreKit

class StoreManager: NSObject, ObservableObject, SKProductsRequestDelegate, SKPaymentTransactionObserver {
    
    //FETCH PRODUCTS
    var request: SKProductsRequest!
    
    @Published var myProducts = [SKProduct]()
    
    func getProducts(productIDs: [String]) {
        print("Start requesting products ...")
        let request = SKProductsRequest(productIdentifiers: Set(productIDs))
        request.delegate = self
        request.start()
    }
    
    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        print("Did receive response")
        
        if !response.products.isEmpty {
            for fetchedProduct in response.products {
                DispatchQueue.main.async {
                    self.myProducts.append(fetchedProduct)
                }
            }
        }
        
        for invalidIdentifier in response.invalidProductIdentifiers {
            print("Invalid identifiers found: \(invalidIdentifier)")
        }
    }
    
    func request(_ request: SKRequest, didFailWithError error: Error) {
        print("Request did fail: \(error)")
    }
    
    //HANDLE TRANSACTIONS
    @Published var transactionState: SKPaymentTransactionState?
    
    func purchaseProduct(product: SKProduct) {
        if SKPaymentQueue.canMakePayments() {
            let payment = SKPayment(product: product)
            SKPaymentQueue.default().add(payment)
        } else {
            print("User can't make payment.")
        }
    }
    
    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        for transaction in transactions {
            switch transaction.transactionState {
            case .purchasing:
                transactionState = .purchasing
            case .purchased:
                UserDefaults.standard.setValue(true, forKey: transaction.payment.productIdentifier)
                queue.finishTransaction(transaction)
                transactionState = .purchased
            case .restored:
                UserDefaults.standard.setValue(true, forKey: transaction.payment.productIdentifier)
                queue.finishTransaction(transaction)
                transactionState = .restored
            case .failed, .deferred:
                print("Payment Queue Error: \(String(describing: transaction.error))")
                queue.finishTransaction(transaction)
                transactionState = .failed
            default:
                queue.finishTransaction(transaction)
            }
        }
    }
    
    func restoreProducts() {
        print("Restoring products ...")
        SKPaymentQueue.default().restoreCompletedTransactions()
    }
}

Upvotes: 4

Views: 4675

Answers (1)

Tarun Tyagi
Tarun Tyagi

Reputation: 10102

You can try this -

if let url = Bundle.main.appStoreReceiptURL, 
   let data = try? Data(contentsOf: url) {
      let receiptBase64 = data.base64EncodedString()
      // Send to server
}

UPDATE

Wondering what your would recommend if eg the connection is interrupted before the receipt strings is sent to my server and for some reason the user deletes the app or something before I can process it.

  1. Store a true value in UserDefaults for a variable called needsToSendPurchaseReceiptToServer .
  2. Update this to false after successful completion of the API call.
  3. In case the api call fails, DO NOT update the UserDefaults value.
  4. Next time your app is launched / or resumed from background appDidBecomeActive callback, you check for this flag and if it is still true, try sending the receipt to server again.
  5. There should be no need to provide any notifications to the user.
  6. In case this fails multiple times, user should still be able to manually trigger this via Restore Purchase option.

Upvotes: 7

Related Questions