[in_app_purchase_storekit] Fixes manual invocation of `finishTransaction()` triggering fatal crash (#8071)

Fixes https://github.com/flutter/flutter/issues/154763

From the Apple docs: 
https://developer.apple.com/documentation/storekit/skpaymentqueue/1506003-finishtransaction
`If you call finishTransaction(_:) on a transaction that is in the [SKPaymentTransactionState.purchasing](https://developer.apple.com/documentation/storekit/skpaymenttransactionstate/purchasing) state, StoreKit raises an exception.`

For some reason even though the old Obj-C implementation didn't have this check, it didn't crash. This adds an explicit check for the purchasing state.
diff --git a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md
index 5f5456c..3d4cc8d 100644
--- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md
+++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.3.20
+
+* Fixes manual invocation of `finishTransaction` causing a fatal crash.
+
 ## 0.3.19+1
 
 * Removes unneeded platform availability annotations.
diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift
index 23b8972..8954f2f 100644
--- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift
+++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift
@@ -253,16 +253,25 @@
     let pendingTransactions = getPaymentQueueHandler().getUnfinishedTransactions()
 
     for transaction in pendingTransactions {
-      // If the user cancels the purchase dialog we won't have a transactionIdentifier.
-      // So if it is null AND a transaction in the pendingTransactions list has
-      // also a null transactionIdentifier we check for equal product identifiers.
-      if transaction.transactionIdentifier == transactionIdentifier
-        || (transactionIdentifier == nil
-          && transaction.transactionIdentifier == nil
-          && transaction.payment.productIdentifier == productIdentifier)
-      {
-        getPaymentQueueHandler().finish(transaction)
+      // finishTransaction() cannot be called on a Transaction with a current purchasing state
+      // https://developer.apple.com/documentation/storekit/skpaymentqueue/1506003-finishtransaction
+      guard transaction.transactionState != SKPaymentTransactionState.purchasing else {
+        continue
       }
+
+      // If the user cancels the purchase dialog we won't have a transactionIdentifier.
+      // So if transactionIdentifier is null AND a transaction in the pendingTransactions list
+      // also has a null transactionIdentifier, we check for equal product identifiers.
+      // TODO(louisehsu): See if we can check for SKErrorPaymentCancelled instead.
+      let matchesTransactionIdentifier = transaction.transactionIdentifier == transactionIdentifier
+      let isCancelledTransaction =
+        transactionIdentifier == nil && transaction.transactionIdentifier == nil
+        && transaction.payment.productIdentifier == productIdentifier
+
+      guard matchesTransactionIdentifier || isCancelledTransaction else {
+        continue
+      }
+      getPaymentQueueHandler().finish(transaction)
     }
   }
 
diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.swift b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.swift
index a074f13..dd91bc6 100644
--- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.swift
+++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.swift
@@ -135,6 +135,46 @@
     XCTAssertNil(error)
   }
 
+  func testFinishTransactionNotCalledOnPurchasingTransactions() {
+    let args: [String: Any] = [
+      "transactionIdentifier": NSNull(),
+      "productIdentifier": "unique_identifier",
+    ]
+
+    let paymentMap: [String: Any] = [
+      "productIdentifier": "123",
+      "requestData": "abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh",
+      "quantity": 2,
+      "applicationUsername": "app user name",
+      "simulatesAskToBuyInSandbox": false,
+    ]
+
+    let transactionMap: [String: Any] = [
+      "transactionState": SKPaymentTransactionState.purchasing.rawValue,
+      "payment": paymentMap,
+      "error": FIAObjectTranslator.getMapFrom(
+        NSError(domain: "test_stub", code: 123, userInfo: [:])),
+      "transactionTimeStamp": NSDate().timeIntervalSince1970,
+    ]
+
+    let paymentTransactionStub = SKPaymentTransactionStub(map: transactionMap)
+
+    let handler = PaymentQueueHandlerStub()
+    plugin.paymentQueueHandler = handler
+
+    var finishTransactionInvokeCount = 0
+
+    handler.finishTransactionStub = { _ in
+      finishTransactionInvokeCount += 1
+    }
+
+    var error: FlutterError?
+    plugin.finishTransactionFinishMap(args, error: &error)
+
+    XCTAssertNil(error)
+    XCTAssertEqual(finishTransactionInvokeCount, 0)
+  }
+
   func testGetProductResponseWithRequestError() {
     let argument = ["123"]
     let expectation = self.expectation(description: "completion handler successfully called")
diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml
index 71b78b1..aa3c1d7 100644
--- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml
+++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml
@@ -2,7 +2,7 @@
 description: An implementation for the iOS and macOS platforms of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework.
 repository: https://github.com/flutter/packages/tree/main/packages/in_app_purchase/in_app_purchase_storekit
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22
-version: 0.3.19+1
+version: 0.3.20
 
 environment:
   sdk: ^3.3.0