Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.4.10

* Fixes StoreKit 2 date format does not match in_app_purchase_platform_interface PurchaseDetails.transactionDate format.
Fixes both `SK2Transaction.purchaseDate` and `SK2Transaction.expirationDate`.

## 0.4.9

* Add support for offer codes in StoreKit 2.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,16 +194,12 @@ extension Product.PurchaseResult {
extension Transaction {
func convertToPigeon(receipt: String?, status: SK2PurchaseStatusMessage) -> SK2TransactionMessage
{

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

return SK2TransactionMessage(
id: Int64(id),
originalId: Int64(originalID),
productId: productID,
purchaseDate: dateFormatter.string(from: purchaseDate),
expirationDate: expirationDate.map { dateFormatter.string(from: $0) },
purchaseDate: String(Int64(purchaseDate.timeIntervalSince1970 * 1000)),
expirationDate: expirationDate.map { String(Int64($0.timeIntervalSince1970 * 1000)) },
Comment on lines +201 to +202
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

When converting from a floating-point TimeInterval (seconds) to integer milliseconds, it is safer to use .rounded() before casting to Int64. This prevents potential off-by-one errors due to floating-point precision (e.g., a value like 0.999999 being truncated to 0 instead of rounded to 1).

Additionally, although Transaction is difficult to instantiate for testing, you could make this logic testable by extracting it into a small helper extension on Date (e.g., var millisecondsSince1970: String).

Suggested change
purchaseDate: String(Int64(purchaseDate.timeIntervalSince1970 * 1000)),
expirationDate: expirationDate.map { String(Int64($0.timeIntervalSince1970 * 1000)) },
purchaseDate: String(Int64((purchaseDate.timeIntervalSince1970 * 1000.0).rounded())),
expirationDate: expirationDate.map { String(Int64(($0.timeIntervalSince1970 * 1000.0).rounded())) },

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'll let @LouiseHsu comment on the larger design question, but if this should be passed as a timestamp it should absolutely not be a String at the Pigeon layer. If it needs to be a string at the Dart level, that should be converted in Dart. The Pigeon interface should have a structured and clearly documented value; if it's a timestamp, that should be a numeric value. Serializing a number as a string rather than converting it on the Dart side is needlessly inefficient, and also makes the Pigeon layer more confusing.

purchasedQuantity: Int64(purchasedQuantity),
appAccountToken: appAccountToken?.uuidString,
receiptData: receipt,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: in_app_purchase_storekit
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.4.9
version: 0.4.10

environment:
sdk: ^3.10.0
Expand Down