iOS: Purchases.purchasePackage({ aPackage: packageToPurchase }) hangs.
I'd first like to mention that this is working on Android. Only fails on iOS.
- [x] I have updated Purchases SDK to the latest version
- [x] I have searched the Community
- [x] I have read docs.revenuecat.com
- [x] I have searched for existing Github issues
Describe the bug Environment package.json...
"dependencies": {
"@capacitor/android": "^7.1.0",
"@capacitor/cli": "^7.1.0",
"@capacitor/core": "^7.1.0",
"@capacitor/filesystem": "^7.0.0",
"@capacitor/ios": "^7.1.0",
"@capacitor/keyboard": "^7.0.0",
"@capacitor/local-notifications": "^7.0.0",
"@capacitor/preferences": "^7.0.0",
"@capacitor/share": "^7.0.0",
"@headlessui/vue": "^1.7.23",
"@heroicons/vue": "^2.2.0",
"@revenuecat/purchases-capacitor": "^10.2.2",
"axios": "^1.7.7",
"marked": "^15.0.2",
"pinia": "^2.2.6",
"status-bar-height": "file:status-bar-height",
"vue": "^3.5.12",
"vue-matomo": "^4.2.0",
"vue-router": "^4.4.5"
},
StoreKit version: 2 Device and/or simulator: - [x] Device - [x] Simulator Environment: - [x] Sandbox - [x] TestFlight - [x] Production How widespread is the issue. Percentage of devices affected. I would suspect it happens on all devices? Or maybe I'm the only one? Same result on all that I've tested.
Debug logs that reproduce the issue. Complete logs with Purchases.logLevel = .verbose will help us debug this issue.
I've replaced anything that looks like a key with xxxxxxxxxxxxx
VERBOSE: Creating intermediate key with expiration '2025-06-11 00:00:00 +0000': xxxxxxxxxxxxxx
VERBOSE: Signature passed verification
VERBOSE: Storing etag '4d88e28e0bb7da8a' for request to 'https://api.revenuecat.com/v1/subscribers/$RCAnonymousID%xxxxxxxxxxxxxxxx/offerings' (success)
DEBUG: ℹ️ API request completed: GET '/v1/subscribers/$RCAnonymousID%xxxxxxxxxxxxxxxxx/offerings' (304)
Request-ID: 'xxxxxxxxxx'; Amzn-Trace-ID: 'Root=xxxxxxxxxxxxx'
DEBUG: ℹ️ No existing products cached, starting store products request for: ["full_monthly", "full_yearly"]
DEBUG: ℹ️ GetOfferingsOperation: Finished
DEBUG: ℹ️ Serial request done: GET /v1/subscribers/$RCAnonymousID%xxxxxxxxxxxxxx/offerings, 0 requests left in the queue
DEBUG: ℹ️ GetCustomerInfoOperation: Started
DEBUG: ℹ️ There are no requests currently running, starting request GET /v1/subscribers/$RCAnonymousID%xxxxxxxxxxxxx
VERBOSE: Using etag 'd3c1e29e6a8cdf11' for request to 'https://api.revenuecat.com/v1/subscribers/$RCAnonymousID%xxxxxxxxxxxxxx'. Validation time: 2025-03-31 14:33:21 +0000
DEBUG: ℹ️ API request started: GET '/v1/subscribers/$RCAnonymousID%xxxxxxxxxxxxx'
VERBOSE: Creating intermediate key with expiration '2025-06-11 00:00:00 +0000': xxxxxxxxxxxxxx
VERBOSE: Signature passed verification
VERBOSE: Storing etag 'd3c1e29e6a8cdf11' for request to 'https://api.revenuecat.com/v1/subscribers/$RCAnonymousID%xxxxxxxxxxxxx' (createdSuccess)
DEBUG: ℹ️ API request completed: GET '/v1/subscribers/$RCAnonymousID%xxxxxxxxxxxxx' (304)
Request-ID: 'efa8beb8-0153-4ac6-8625-1c9c38c352d5'; Amzn-Trace-ID: 'Root=1-67eaa824-7bbd95120e61544c12b105fe'
VERBOSE: Updating CustomerInfo '$RCAnonymousID:xxxxxxxxxxxxx' request date: 2025-03-31 14:35:16 +0000
DEBUG: 😻 CustomerInfo updated from network.
DEBUG: ℹ️ GetCustomerInfoOperation: Finished
DEBUG: ℹ️ Serial request done: GET /v1/subscribers/$RCAnonymousID%xxxxxxxxxxxxx, 0 requests left in the queue
DEBUG: 😻 Store products request received response
DEBUG: ℹ️ Store products request finished
DEBUG: 😻 Offerings updated from network.
DEBUG: ℹ️ Warming up intro eligibility cache for 2 products
DEBUG: ℹ️ No existing products cached, starting store products request for: ["full_monthly", "full_yearly"]
VERBOSE: Warming up paywall images cache: [https://assets.pawwalls.com/1151531_1737931765.heic]
DEBUG: 😻 Store products request received response
DEBUG: ℹ️ Store products request finished
DEBUG: ℹ️ Caching trial or intro eligibility for products: ["full_yearly", "full_monthly"]
⚡️ [error] - There was an error setting cookie `_pk_ref.6.1fff`. Please check domain and path.
⚡️ [error] - There was an error setting cookie `_pk_id.6.1fff`. Please check domain and path.
⚡️ [error] - There was an error setting cookie `_pk_ses.6.1fff`. Please check domain and path.
⚡️ To Native -> Purchases getOfferings 84254070
DEBUG: ℹ️ Vending Offerings from memory cache
⚡️ TO JS {"all":{"Standard Offering.":{"monthly":{"identifier":"$rc_monthly","product":{"pricePerMonth":12.99,"title":"Monthly Subscription","currencyCode":"USD","introPrice":null,"pricePerYearString":"$155.88","productCategory":"SUBSCRIPTION","description":"","pri
⚡️ [log] - Offerings: {"all":{"Standard Offering.":{"monthly":{"identifier":"$rc_monthly","product":{"pricePerMonth":12.99,"title":"Monthly Subscription","currencyCode":"USD","introPrice":null,"pricePerYearString":"$155.88","productCategory":"SUBSCRIPTION","description":"","pricePerYear":155.88,"discounts":[],"pricePerMonthString":"$12.99","subscriptionPeriod":"P1M","identifier":"full_monthly","pricePerWeekString":"$2.99","price":12.99,"productType":"AUTO_RENEWABLE_SUBSCRIPTION","priceString":"$12.99","pricePerWeek":2.99},"packageType":"MONTHLY","offeringIdentifier":"Standard Offering.","presentedOfferingContext":{"targetingContext":null,"offeringIdentifier":"Standard Offering.","placementIdentifier":null}},"annual":{"product":{"description":"","pricePerYearString":"$129.99","productType":"AUTO_RENEWABLE_SUBSCRIPTION","title":"Yearly Subscription","currencyCode":"USD","pricePerYear":129.99,"subscriptionPeriod":"P1Y","identifier":"full_yearly","pricePerMonth":10.83,"productCategory":"SUBSCRIPTION","discounts":[],"price":129.99,"pricePerWeek":2.49,"priceString":"$129.99","pricePerWeekString":"$2.49","introPrice":null,"pricePerMonthString":"$10.83"},"offeringIdentifier":"Standard Offering.","identifier":"$rc_annual","packageType":"ANNUAL","presentedOfferingContext":{"offeringIdentifier":"Standard Offering.","placementIdentifier":null,"targetingContext":null}},"availablePackages":[{"offeringIdentifier":"Standard Offering.","presentedOfferingContext":{"placementIdentifier":null,"targetingContext":null,"offeringIdentifier":"Standard Offering."},"identifier":"$rc_annual","packageType":"ANNUAL","product":{"discounts":[],"description":"","identifier":"full_yearly","pricePerYear":129.99,"productType":"AUTO_RENEWABLE_SUBSCRIPTION","currencyCode":"USD","subscriptionPeriod":"P1Y","pricePerMonth":10.83,"productCategory":"SUBSCRIPTION","priceString":"$129.99","introPrice":null,"price":129.99,"pricePerWeek":2.49,"pricePerWeekString":"$2.49","pricePerYearString":"$129.99","pricePerMonthString":"$10.83","title":"Yearly Subscription"}},{"offeringIdentifier":"Standard Offering.","presentedOfferingContext":{"targetingContext":null,"placementIdentifier":null,"offeringIdentifier":"Standard Offering."},"packageType":"MONTHLY","identifier":"$rc_monthly","product":{"productCategory":"SUBSCRIPTION","pricePerYear":155.88,"discounts":[],"productType":"AUTO_RENEWABLE_SUBSCRIPTION","title":"Monthly Subscription","currencyCode":"USD","subscriptionPeriod":"P1M","identifier":"full_monthly","pricePerMonthString":"$12.99","pricePerYearString":"$155.88","pricePerWeek":2.99,"pricePerWeekString":"$2.99","pricePerMonth":12.99,"description":"","introPrice":null,"priceString":"$12.99","price":12.99}}],"metadata":{},"identifier":"Standard Offering.","serverDescription":"The standard set of packages"}},"current":{"availablePackages":[{"identifier":"$rc_annual","packageType":"ANNUAL","offeringIdentifier":"Standard Offering.","presentedOfferingContext":{"placementIdentifier":null,"offeringIdentifier":"Standard Offering.","targetingContext":null},"product":{"identifier":"full_yearly","pricePerWeek":2.49,"pricePerYear":129.99,"discounts":[],"priceString":"$129.99","currencyCode":"USD","productCategory":"SUBSCRIPTION","title":"Yearly Subscription","subscriptionPeriod":"P1Y","productType":"AUTO_RENEWABLE_SUBSCRIPTION","price":129.99,"pricePerWeekString":"$2.49","pricePerMonth":10.83,"pricePerMonthString":"$10.83","description":"","pricePerYearString":"$129.99","introPrice":null}},{"offeringIdentifier":"Standard Offering.","product":{"pricePerYearString":"$155.88","price":12.99,"identifier":"full_monthly","description":"","pricePerWeek":2.99,"pricePerWeekString":"$2.99","productCategory":"SUBSCRIPTION","productType":"AUTO_RENEWABLE_SUBSCRIPTION","title":"Monthly Subscription","pricePerMonthString":"$12.99","priceString":"$12.99","subscriptionPeriod":"P1M","currencyCode":"USD","discounts":[],"pricePerMonth":12.99,"introPrice":null,"pricePerYear":155.88},"identifier":"$rc_monthly","presentedOfferingContext":{"placementIdentifier":null,"targetin
⚡️ [log] - purchasePackage {"offeringIdentifier":"Standard Offering.","product":{"pricePerYearString":"$155.88","price":12.99,"identifier":"full_monthly","description":"","pricePerWeek":2.99,"pricePerWeekString":"$2.99","productCategory":"SUBSCRIPTION","productType":"AUTO_RENEWABLE_SUBSCRIPTION","title":"Monthly Subscription","pricePerMonthString":"$12.99","priceString":"$12.99","subscriptionPeriod":"P1M","currencyCode":"USD","discounts":[],"pricePerMonth":12.99,"introPrice":null,"pricePerYear":155.88},"identifier":"$rc_monthly","presentedOfferingContext":{"placementIdentifier":null,"targetingContext":null,"offeringIdentifier":"Standard Offering."},"packageType":"MONTHLY"}
Steps to reproduce, with a description of expected vs. actual behavior
✅ Initialization works.
✅ getOfferings works.
❌ A product is selected and passed to purchasePackage. I would expect RevCat to pop a payment completion view, like it does on the Android side. Instead, it hangs indefinitely.
For example:
await RevenueCatService.purchasePackage ({
"presentedOfferingContext": {
"offeringIdentifier": "Standard Offering.",
"placementIdentifier": null,
"targetingContext": null
},
"offeringIdentifier": "Standard Offering.",
"product": {
"priceString": "$12.99",
"pricePerMonthString": "$12.99",
"pricePerWeek": 2.99,
"pricePerYear": 155.88,
"pricePerYearString": "$155.88",
"pricePerMonth": 12.99,
"title": "Monthly Subscription",
"price": 12.99,
"productType": "AUTO_RENEWABLE_SUBSCRIPTION",
"subscriptionPeriod": "P1M",
"currencyCode": "USD",
"discounts": [],
"pricePerWeekString": "$2.99",
"productCategory": "SUBSCRIPTION",
"introPrice": null,
"description": "",
"identifier": "full_monthly"
},
"identifier": "$rc_monthly",
"packageType": "MONTHLY"
})
This is the RevenueCatService.purchasePackage function:
static async purchasePackage(packageToPurchase) {
try{
const result = await Purchases.purchasePackage({ aPackage: packageToPurchase })
console.log("Purchase Result:", result) //never gets here.
return result
}catch (e){
console.log("revenuecat.purchasePackage Failed: ", e)
}
}
Uploaded some images here: https://community.revenuecat.com/general-questions-7/ios-capacitor-plugin-purchases-purchasepackage-apackage-packagetopurchase-hangs-6161
👀 We've just linked this issue to our internal tracker and notified the team. Thank you for reporting, we're checking this out!
To help illustrate, on android the payment view appears. I would expect the equivalent action in iOS, but it hangs silently. No errors.
Update on this. Instead of waiting for this to get fixed, I decided to roll my own Capacitor plugin for PlayStore and StoreKit. I got it working for Android first, then I got stuck on iOS in the exact same spot the RevenueCat plugin was failing. I added a ton of logs, trying to figure out what was keeping getOffers from returning a result. So, I started over again, with https://github.com/jordancalhoun/StoreKit2 as a working pay flow example. Its working now!
Still not 100% sure what the deal was, but I suspect it has something to do with the way getOfferings returns it's results. Perhaps a change in StoreKit2? If this is not a widespread issue, it could be due to using a VueJS frontend.
Anyways, do you want me to close this?
Hi @snovak, Thank you for sharing it! Do you know with which iOS version where you testing it with?
This is still an issue - the call never returns. I am on the latest iOS version - 18.3.2
I have resolved this by using toRaw() on the function call.
Oh! Thanks for looking into this @hahagu ! I see my error. I was unintentionally passing in the selectedProduct.value (a VueJS reactive proxy), and I need toRaw() or something like {...selectedProduct.value} for it to work.
If I were to request anything here it might be better logging, letting me know my input wasn't matching expectations.
Thanks again!
Yes, I do agree a better logging would've helped me out a ton @snovak . Also weird that it works on the Android side but not on iOS.
Very weird @hahagu . That's probably what threw me. I was more focused on StoreKit being the culprit since android was working.
Summarizing the issue here:
This stems from passing a Vue Proxy-wrapped object (from Vue’s reactivity system) into a Capacitor-native bridge. These native bridges can’t serialize Proxy objects, so the call silently fails — it never resolves or rejects.
To confirm this is the case, you can check whether your product object is reactive like this:
import { isProxy } from 'vue';
console.log('Is proxy?', isProxy(this.products[productId]));
If it logs true, that means you’re passing a Proxy and need to unwrap it before passing it into purchasePackage.
To resolve the issue, you could do:
import { toRaw } from 'vue';
// …
let rawPackage = toRaw(this.products[productId]);
let result = await Purchases.purchasePackage({ aPackage: rawPackage });
This will strip away the Proxy wrapper and pass the raw object into the RevenueCat SDK.