Skip to content
This repository was archived by the owner on Oct 16, 2025. It is now read-only.

Commit ea92051

Browse files
hyochanclaude
andcommitted
fix: improve promotional offer error handling and logging
- Add detailed error logging for invalid nonce and signature formats - Throw explicit error when promotional offer validation fails - Change purchaseOptions to throw errors instead of silently ignoring invalid offers - Add debug logging to track promotional offer creation This fix helps developers identify issues with promotional offer signatures and prevents silent failures when offers are invalid. Fixes promotional offer "Unable to Purchase" errors by providing clear error messages when nonce is not a valid UUID or signature is not base64 encoded. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent d0bffd4 commit ea92051

File tree

3 files changed

+21
-6
lines changed

3 files changed

+21
-6
lines changed

Sources/Helpers/StoreKitTypesBridge.swift

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,15 +167,22 @@ enum StoreKitTypesBridge {
167167
return nil
168168
}
169169

170-
static func purchaseOptions(from props: RequestPurchaseIosProps) -> Set<StoreKit.Product.PurchaseOption> {
170+
static func purchaseOptions(from props: RequestPurchaseIosProps) throws -> Set<StoreKit.Product.PurchaseOption> {
171171
var options: Set<StoreKit.Product.PurchaseOption> = []
172172
if let quantity = props.quantity, quantity > 1 {
173173
options.insert(.quantity(quantity))
174174
}
175175
if let token = props.appAccountToken, let uuid = UUID(uuidString: token) {
176176
options.insert(.appAccountToken(uuid))
177177
}
178-
if let offerInput = props.withOffer, let option = promotionalOffer(from: offerInput) {
178+
if let offerInput = props.withOffer {
179+
guard let option = promotionalOffer(from: offerInput) else {
180+
throw PurchaseError.make(
181+
code: .developerError,
182+
productId: props.sku,
183+
message: "Invalid promotional offer: nonce must be valid UUID and signature must be base64 encoded"
184+
)
185+
}
179186
options.insert(option)
180187
}
181188
return options
@@ -278,11 +285,19 @@ private extension StoreKitTypesBridge {
278285
}
279286

280287
static func promotionalOffer(from offer: DiscountOfferInputIOS) -> StoreKit.Product.PurchaseOption? {
281-
guard let nonce = UUID(uuidString: offer.nonce),
282-
let signature = Data(base64Encoded: offer.signature) else {
288+
guard let nonce = UUID(uuidString: offer.nonce) else {
289+
OpenIapLog.error("❌ Invalid nonce format: \(offer.nonce)")
290+
return nil
291+
}
292+
293+
guard let signature = Data(base64Encoded: offer.signature) else {
294+
OpenIapLog.error("❌ Invalid signature format (must be base64): \(offer.signature)")
283295
return nil
284296
}
297+
285298
let timestamp = Int(offer.timestamp)
299+
OpenIapLog.debug("✅ Creating promotional offer - ID: \(offer.identifier), KeyID: \(offer.keyIdentifier), Timestamp: \(timestamp)")
300+
286301
return .promotionalOffer(
287302
offerID: offer.identifier,
288303
keyID: offer.keyIdentifier,

Sources/OpenIapModule.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ public final class OpenIapModule: NSObject, OpenIapModuleProtocol {
170170
let iosProps = try resolveIosPurchaseProps(from: params)
171171
let sku = iosProps.sku
172172
let product = try await storeProduct(for: sku)
173-
let options = StoreKitTypesBridge.purchaseOptions(from: iosProps)
173+
let options = try StoreKitTypesBridge.purchaseOptions(from: iosProps)
174174

175175
let result: StoreKit.Product.PurchaseResult
176176
#if canImport(UIKit)

openiap-versions.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
2-
"apple": "1.2.11",
2+
"apple": "1.2.12",
33
"gql": "1.0.12"
44
}

0 commit comments

Comments
 (0)