5

I have a issue at the moment where a user can not make a purchase on the Apple App Store in BOTH Production and Testing, which rules out Sandbox accounts etc (have also dived down said route). The same code is working perfectly for Android.

Things I have explored/tried:

Deleting and creating sandbox accounts for Apple. Checked I have no outstanding agreements with Apple. Made sure in-app purchases in enabled. Tried configuring the purchases_flutter package with both legacy and new api key. I am sure a ton more, that I don’t remember. The error looks as follows:

flutter: \^[[38;5;12m│  now trying to purchase<…>
[Purchases] - DEBUG: ℹ️ Vending Offerings from cache
[Purchases] - DEBUG: ℹ️ makePurchase
[Purchases] - DEBUG:  Purchasing product from package  - finMonitor_1500_1y_1w0 in Offering App Access
[Purchases] - DEBUG: ℹ️ PaymentQueue updatedTransaction: finMonitor_1500_1y_1w0 (null) ((null)) (null) - 0
<SKPaymentQueue: 0x2824dfa00>: Payment completed with error: Error Domain=ASDErrorDomain Code=500 "Unhandled exception" UserInfo={NSUnderlyingError=0x282863960 {Error Domain=AMSErrorDomain Code=301 "Invalid Status Code" UserInfo={NSLocalizedDescription=Invalid Status Code, NSLocalizedFailureReason=The response has an invalid status code}}, NSLocalizedFailureReason=An unknown error occurred, NSLocalizedDescription=Unhandled exception}
[Purchases] - DEBUG: ℹ️ PaymentQueue updatedTransaction: finMonitor_1500_1y_1w0 (null) (Error Domain=SKErrorDomain Code=0 "An unknown error occurred" UserInfo={NSLocalizedDescription=An unknown error occurred, NSUnderlyingError=0x2828602a0 {Error Domain=ASDErrorDomain Code=500 "Unhandled exception" UserInfo={NSUnderlyingError=0x282863960 {Error Domain=AMSErrorDomain Code=301 "Invalid Status Code" UserInfo={NSLocalizedDescription=Invalid Status Code, NSLocalizedFailureReason=The response has an invalid status code}}, NSLocalizedFailureReason=An unknown error occurred, NSLocalizedDescription=Unhandled exception}}}) (null) - 2
[Purchases] - ERROR: ‼️ There was a problem with the App Store.
[Purchases] - DEBUG:  Finishing transaction finMonitor_1500_1y_1w0 (null) ((null))
[Purchases] - DEBUG: ℹ️ PaymentQueue removedTransaction: finMonitor_1500_1y_1w0 (null) ((null) Error Domain=SKErrorDomain Code=0 "An unknown error occurred" UserInfo={NSLocalizedDescription=An unknown error occurred, NSUnderlyingError=0x2828602a0 {Error Domain=ASDErrorDomain Code=500 "Unhandled exception" UserInfo={NSUnderlyingError=0x282863960 {Error Domain=AMSErrorDomain Code=301 "Invalid Status Code" UserInfo={NSLocalizedDescription=Invalid Status Code, NSLocalizedFailureReason=The response has an invalid status code}}, NSLocalizedFailureReason=An unknown error occurred, NSLocalizedDescription=Unhandled exception}}}) {
    NSLocalizedDescription = "An unknown error occurred";
    NSUnderlyingError = "Error Domain=ASDErrorDomain Code=500 \"Unhandled exception\" UserInfo={NSUnderlyingError=0x282863960 {Error Domain=AMSErrorDomain Code=301 \"Invalid Status Code\" UserInfo={NSLocalizedDescription=Invalid Status Code, NSLocalizedFailureReason=The response has an invalid status code}}, NSLocalizedFailureRea

flutter: \^[[38;5;196m│ ⛔ Paywall Upgrade - Exception caught: Error code PurchasesErrorCode.storeProblemError<…>

Here is the code I am using to make the purchase:

onPressed: () async {  
showLoadingBanner(context, 'Purchase is progress...');  
try {    
loggerInfo(logMessage: 'now trying to purchase');    
_purchaserInfo = await Purchases.purchasePackage(widget.package);    

loggerInfo(logMessage: 'purchase completed');    
paywallData.isPro = _purchaserInfo.entitlements.all["App Access"]!.isActive;    

loggerDebug(logMessage: 'is user pro? ${paywallData.isPro}');
The code continues for some dialog handling etc, but it never reaches the loggerInfo(logMessage: 'purchase completed');
       

The widget.package is passed from the widget above, and is fetched as follows:

return FutureBuilder(
        future: _fetchOfferings(),
        builder: (BuildContext context, AsyncSnapshot snapshot) {
          if (snapshot.connectionState == ConnectionState.done && snapshot.data != null) {
            Offerings offerings = snapshot.data;
            final offering = offerings.current;
            if (offering != null) {
              final annual = offering.annual;
              if (annual != null) {
                return TopBarAgnosticNoIcon(
                  text: "Welcome!",
                  style: kSendButtonTextStyle(context),
                  uniqueHeroTag: 'purchase_screen',
                  child: Scaffold(
                      backgroundColor: Theme.of(context).backgroundColor,

                      body: Stack(children: [
                        Center(
                          child: SingleChildScrollView(
                              child: Column(
                            mainAxisSize: MainAxisSize.min,
                            mainAxisAlignment: MainAxisAlignment.start,
                            crossAxisAlignment: CrossAxisAlignment.center,
                            children: <Widget>[
                              Padding(
                                padding: const EdgeInsets.all(18.0),
                                child: Image.asset("assets/logos/finMonitor_Transparent.png"),
                              ),
                              Text(
                                'Select the subscription plan to get started.',
                                textAlign: TextAlign.center,
                                style: kSendButtonTextStyle(context),
                              ),
                              Padding(
                                padding: const EdgeInsets.all(8.0),
                                child: PurchaseButton(package: annual),

The code used for the _fetchOfferings future function is:

Future<Offerings?> _fetchOfferings() async {
    Offerings? offerings;
    try {
      offerings = await Purchases.getOfferings();
    } on PlatformException catch (e) {
      loggerError(logMessage: 'Paywall Upgrade - _fetchOfferings: Exception caught $e');
      if (Platform.isAndroid) {
        _showSnackBar(context, 'Issue fetching information for purchase, please check you are signed into the play store');
      } else {
        _showSnackBar(context, 'Issue fetching information for purchase, please check you are signed into the app store');
      }
    }
    return offerings;
  }

Lastly the the SDK is configured as shown in the documentation, with a few conditional tweaks:

    await Purchases.setDebugLogsEnabled(true)
        .whenComplete(() => loggerInfo(logMessage: 'setDebugLogsEnabled completed'));
    if (Platform.isAndroid){
      if ((username != null && username != '') && (password != null && password != '') && (custKey != null && custKey != '')) {
        await Purchases.setup(apiAndroidRevenueKey, appUserId: '$username-$custKey');
      }else{
        await Purchases.setup(apiAndroidRevenueKey);
      }

    }
    else if (Platform.isIOS){
      if ((username != null && username != '') && (password != null && password != '') && (custKey != null && custKey != '')) {
        await Purchases.setup(apiIosRevenueKey, appUserId: '$username-$custKey');
      }else{
        await Purchases.setup(apiIosRevenueKey);
      }

    loggerInfo(logMessage: 'After purchases.setup');
  }

Any help will be hugely appreciated.

An_Alpaca
  • 141
  • 4
  • You get a `301` status code back. It is a "Moved Permanently redirect status response". So perhaps your endpoint/link needs to updated for your service. – Apealed Feb 07 '22 at 14:52
  • @Apealed thank so much for your response, will definitely take a look into that today! Sadly I haven't had any other helpful responses elsewhere, so anything to go on is helpful – An_Alpaca Feb 08 '22 at 07:08

0 Answers0