fastlane / fastlane

🚀 The easiest way to automate building and releasing your iOS and Android apps
https://fastlane.tools
MIT License
39.66k stars 5.72k forks source link

Cannot change IAP title and description #14420

Closed hhoweson closed 5 years ago

hhoweson commented 5 years ago

app.in_app_purchases.all returns

<Spaceship::Tunes::IAPList 
    application=<Spaceship::Tunes::Application 
        apple_id="1085013031", 
        name="uTalk - Learn Any Language", 
        vendor_id="UTALKONE", 
        bundle_id="com.eurotalk.utalkone", 
        last_modified=1552573355000, 
        issues_count=0, 
        app_icon_preview_url="https://is4-ssl.mzstatic.com/image/thumb/Purple124/v4/44/d9/cc/44d9cca5-6a7a-14c5-0990-3bb6c86bc1d9/AppIcon-0-1x_U007emarketing-0-85-220-6.png/340x340bb-80.png", 
        version_sets=[<Spaceship::Tunes::VersionSet 
            type="APP", 
            application=<Spaceship::Tunes::Application 
            #<Object ...>>, 
            platform="ios">]>, 
    reference_name="001 Learn English (American) (VA PA)", 
    product_id="ios_utalkone_wholelanguage_va_pa_001", 
    family_reference_name=nil, 
    duration_days=0, 
    versions=[{"screenshotUrl"=>nil, "canSubmit"=>false, "issuesCount"=>0, "itunesConnectStatus"=>"readyForSale"}], 
    purple_apple_id=["1085013031"], 
    last_modified_date=1552239987000, 
    is_news_subscription=false, 
    number_of_codes=0, 
    maximum_number_of_codes=100, 
    app_maximum_number_of_codes=1000, 
    is_editable=false, 
    is_required=false, 
    can_delete_addon=false>

From what I can tell .versions should return all the titles and descriptions for the IAP in different localisations (and let you change them) however it doesn't? Am I misunderstanding how this works or is it broken?

Thanks, Henry

fastlane-bot commented 5 years ago

It seems like you have not included the output of fastlane env

To make it easier for us help you resolve this issue, please update the issue to include the output of fastlane env :+1:

jmei08 commented 5 years ago

It can not change title and description when it is in ReadyForSale status

hhoweson commented 5 years ago

Would it be possible to add this since it is possible through the itc interface? Thanks

max-ott commented 5 years ago

@hhoweson could you try https://github.com/fastlane/fastlane/blob/master/spaceship/lib/spaceship/tunes/iap_detail.rb#L72? What’s th full code you currently run so we can replicate it in IRB? The data you posted seems to be fine. This is similar to all_apps = Spaceship::Tunes::Application.all where you also only get the standard / basic information (read only). If you want to edit anything you need to go „one level deeper“:


details.name['en-US'] = "App Name"
details.privacy_url['en-US'] = "https://fastlane.tools"
details.save!
hhoweson commented 5 years ago

Aaaah, I see now, thank you! I had not realised that more information was available by using .edit, I can now view the titles and descriptions 👍, however I do have an issue editing them. Even if I change nothing I get the following error when I try to save:

/Library/Ruby/Gems/2.3.0/gems/fastlane-2.117.1/spaceship/lib/spaceship/tunes/tunes_client.rb:230:in `handle_itc_response': ITC.response.error.OPERATION_FAILED (Spaceship::Tunes::Error)
    from /Library/Ruby/Gems/2.3.0/gems/fastlane-2.117.1/spaceship/lib/spaceship/tunes/tunes_client.rb:1241:in `block in update_iap!'
    from /Library/Ruby/Gems/2.3.0/gems/fastlane-2.117.1/spaceship/lib/spaceship/tunes/tunes_client.rb:1461:in `with_tunes_retry'
    from /Library/Ruby/Gems/2.3.0/gems/fastlane-2.117.1/spaceship/lib/spaceship/tunes/tunes_client.rb:1235:in `update_iap!'
    from /Library/Ruby/Gems/2.3.0/gems/fastlane-2.117.1/spaceship/lib/spaceship/tunes/iap_detail.rb:182:in `save!'
    from getIaps.rb:198:in `block (2 levels) in <main>'
    from getIaps.rb:87:in `each'
    from getIaps.rb:87:in `block in <main>'
    from getIaps.rb:71:in `each'
    from getIaps.rb:71:in `<main>'

My code is similar to this:

app = Spaceship::Tunes::Application.find(app_id)
iaps = app.in_app_purchases.all
iapReadonly = iaps[0]
iap = iapReadonly.edit
iap.save!

The save command is on line 71 of my code and appear to be the command causing the error

max-ott commented 5 years ago

@hhoweson Could you check ~/tmp for the spaceship log? The trace does not tell much, except spaceship was not able to save your changes.

hhoweson commented 5 years ago
# Logfile created on 2019-03-25 09:12:49 +0000 by logger.rb/56438
INFO  [09:12:49]: >> GET https://olympus.itunes.apple.com/v1/session: [undefined body] 
DEBUG [09:12:50]: << GET https://olympus.itunes.apple.com/v1/session: 200 {"user"=>{"fullName"=>"aditya mehta", "firstName"=>"aditya", "lastName"=>"mehta", "emailAddress"=>"mixpanel@utalk.com", "prsId"=>"16053026469"}, "provider"=>{"providerId"=>108936, "name"=>"EuroTalk Ltd", "contentTypes"=>["SOFTWARE"], "subType"=>"COMPANY"}, "availableProviders"=>[{"providerId"=>108936, "name"=>"EuroTalk Ltd", "contentTypes"=>["SOFTWARE"], "subType"=>"COMPANY"}], "backingType"=>"ITC", "backingTypes"=>["ITC"], "roles"=>["SALES", "MARKETING", "REPORTS", "CUSTOMER_SUPPORT"], "unverifiedRoles"=>[], "featureFlags"=>["showWwdrUserRoles", "adpRad", "apiKeys"], "agreeToTerms"=>true, "termsSignatures"=>["ASC", "RAD"], "modules"=>[{"key"=>"Apps", "name"=>"ITC.HomePage.Apps.IconText", "localizedName"=>"My Apps", "url"=>"https://appstoreconnect.apple.com/WebObjects/iTunesConnect.woa/ra/ng/app", "iconUrl"=>"https://appstoreconnect.apple.com/static/img/ico_homepage/themed/apps/Apps@2x.d3ce493e56172e92aed6.png", "down"=>false, "visible"=>true, "hasNotifications"=>false}, {"key"=>"AppAnalytics", "name"=>"ITC.HomePage.AppAnalytics.IconText", "localizedName"=>"App Analytics", "url"=>"https://analytics.itunes.apple.com/", "iconUrl"=>"https://appstoreconnect.apple.com/static/img/ico_homepage/themed/apps/AppAnalytics@2x.e19f711d943cb42d65b2.png", "down"=>false, "visible"=>true, "hasNotifications"=>false}, {"key"=>"SalesTrends", "name"=>"ITC.HomePage.SalesTrends.IconText", "localizedName"=>"Sales and Trends", "url"=>"https://reportingitc2.apple.com/?", "iconUrl"=>"https://appstoreconnect.apple.com/static/img/ico_homepage/themed/apps/SalesTrends@2x.b1f802112426525d990a.png", "down"=>false, "visible"=>true, "hasNotifications"=>false}, {"key"=>"Account", "name"=>"ITC.HomePage.Account.IconText", "localizedName"=>"Users and Access", "url"=>"https://appstoreconnect.apple.com/access/users", "iconUrl"=>"https://appstoreconnect.apple.com/static/img/ico_homepage/themed/apps/ManageUsers@2x.81511f3933fb2fb4b20d.png", "down"=>false, "visible"=>true, "hasNotifications"=>false}, {"key"=>"Resources", "name"=>"ITC.HomePage.Resources.IconText", "localizedName"=>"Resources and Help", "url"=>"https://developer.apple.com/app-store-connect/", "iconUrl"=>"https://appstoreconnect.apple.com/static/img/ico_homepage/themed/apps/Resources@2x.3c8d0d8c08e876cf9470.png", "down"=>false, "visible"=>true, "hasNotifications"=>false}], "helpLinks"=>[{"key"=>"AllAsc", "url"=>"https://help.apple.com/app-store-connect/", "localizedText"=>"App Store Connect Resources"}, {"key"=>"Xcode", "url"=>"https://help.apple.com/xcode/mac/current/", "localizedText"=>"Xcode Help"}, {"key"=>"DeveloperAccount", "url"=>"https://help.apple.com/developer-account/", "localizedText"=>"Developer Account Help"}, {"key"=>"SupportContact", "url"=>"https://developer.apple.com/support/", "localizedText"=>"Support and Contact"}], "userProfile"=>[{"key"=>"signIn", "url"=>"https://appstoreconnect.apple.com/login", "localizedText"=>"Sign In"}, {"key"=>"personalDetails", "url"=>"https://appstoreconnect.apple.com/access/users/918a9466-8331-4c46-b71b-d3503cf59d37/settings", "localizedText"=>"Edit Profile"}, {"key"=>"signOut", "url"=>"https://appstoreconnect.apple.com/logout", "localizedText"=>"Sign Out"}], "pccDto"=>nil, "publicUserId"=>"918a9466-8331-4c46-b71b-d3503cf59d37"}
INFO  [09:12:50]: >> GET ra/apps/manageyourapps/summary/v2: [undefined body] 
DEBUG [09:12:53]: << GET ra/apps/manageyourapps/summary/v2: 200 {"data"=>{*****}, "messages"=>{"warn"=>nil, "info"=>nil, "error"=>nil}, "statusCode"=>"SUCCESS"}
INFO  [09:12:53]: >> GET ra/apps/1085013031/iaps: [undefined body] 
DEBUG [09:12:54]: << GET ra/apps/1085013031/iaps: 200 {"data"=>[*****], "messages"=>{"warn"=>nil, "info"=>nil, "error"=>nil}, "statusCode"=>"SUCCESS"}
INFO  [09:12:54]: >> GET ra/apps/1085013031/iaps/1407845488: [undefined body] 
DEBUG [09:12:59]: << GET ra/apps/1085013031/iaps/1407845488: 200 {"data"=>{*****}, "messages"=>{"warn"=>nil, "error"=>nil, "info"=>nil}, "statusCode"=>"SUCCESS"}
INFO  [09:12:59]: >> PUT ra/apps/1085013031/iaps/1407845488: {"sectionErrorKeys":[],"sectionInfoKeys":[],"sectionWarningKeys":[],"value":null,"id":"3335729086","adamId":"1407845488","appAdamIds":["1085013031"],"familyId":null,"addOnType":"nonConsumable","isNewsSubscription":false,"isReplaced":false,"replacementAdamId":null,"referenceName":{"value":"001 Learn English (American) (VA PA)","isEditable":true,"isRequired":true,"errorKeys":null,"maxLength":64,"minLength":2},"productId":{"value":"ios_utalkone_wholelanguage_va_pa_001","isEditable":false,"isRequired":true,"errorKeys":null,"maxLength":100,"minLength":2},"clearedForSale":{"value":true,"isEditable":true,"isRequired":false,"errorKeys":null},"pricingDurationType":null,"pricingIntervals":[{"value":{"tierStem":41,"priceTierEffectiveDate":null,"priceTierEndDate":null,"country":"WW","grandfathered":null}}],"ungrandfatheredIntervals":null,"freeTrialDurationType":null,"bonusPeriodDurationType":null,"versions":[{"reviewNotes":{"value":null},"contentHosting":{"value":false,"isEditable":true,"isRequired":false,"errorKeys":null},"details":{"value":[{"value":{"description":{"value":"In app purchase: Американский английский"},"name":{"value":"Американский английский"},"localeCode":"ru"}},{"value":{"description":{"value":"In app purchase: Imparare Inglese americano"},"name":{"value":"Imparare Inglese americano"},"localeCode":"it"}},{"value":{"description":{"value":"In app purchase: Learn English (American)"},"name":{"value":"Learn English (American)"},"localeCode":"en-GB"}},{"value":{"description":{"value":"All categories & topics with lifetime access."},"name":{"value":"Learn English (American)"},"localeCode":"en-US"}},{"value":{"description":{"value":"In app purchase: Amerikanisches Englisch"},"name":{"value":"Amerikanisches Englisch"},"localeCode":"de-DE"}},{"value":{"description":{"value":"In app purchase: Aprender Inglês americano"},"name":{"value":"Aprender Inglês americano"},"localeCode":"pt-BR"}},{"value":{"description":{"value":"In app purchase: Aprenda Inglés estadounidens"},"name":{"value":"Aprenda Inglés estadounidense"},"localeCode":"es-ES"}},{"value":{"description":{"value":"In app purchase: 영어(미국) 공부"},"name":{"value":"영어(미국) 공부"},"localeCode":"ko"}},{"value":{"description":{"value":"In app purchase: Μάθετε Αγγλικά Αμερικής"},"name":{"value":"Μάθετε Αγγλικά Αμερικής"},"localeCode":"el"}},{"value":{"description":{"value":"In app purchase: Apprendre Anglais américain"},"name":{"value":"Apprendre Anglais américain"},"localeCode":"fr-CA"}},{"value":{"description":{"value":"In app purchase: Opi Amerikanenglanti"},"name":{"value":"Opi Amerikanenglanti"},"localeCode":"fi"}},{"value":{"description":{"value":"In app purchase: Learn English (American)"},"name":{"value":"Learn English (American)"},"localeCode":"en-AU"}},{"value":{"description":{"value":"In app purchase: はじめてのアメリカ英語"},"name":{"value":"はじめてのアメリカ英語"},"localeCode":"ja"}},{"value":{"description":{"value":"In app purchase: Lær Amerikansk engelsk"},"name":{"value":"Lær Amerikansk engelsk"},"localeCode":"no"}},{"value":{"description":{"value":"In app purchase: Aprenda Inglês americano"},"name":{"value":"Aprenda Inglês americano"},"localeCode":"pt-PT"}},{"value":{"description":{"value":"In app purchase: Amerikan İngilizcesi öğrenme"},"name":{"value":"Amerikan İngilizcesi öğrenmek"},"localeCode":"tr"}},{"value":{"description":{"value":"In app purchase: อังกฤษ - อเมริกัน"},"name":{"value":"อังกฤษ - อเมริกัน"},"localeCode":"th"}},{"value":{"description":{"value":"In app purchase: Learn English (American)"},"name":{"value":"Learn English (American)"},"localeCode":"en-CA"}},{"value":{"description":{"value":"In app purchase: Apprendre Anglais américain"},"name":{"value":"Apprendre Anglais américain"},"localeCode":"fr-FR"}},{"value":{"description":{"value":"In app purchase: Tiếng Anh (Mỹ)"},"name":{"value":"Tiếng Anh (Mỹ)"},"localeCode":"vi"}},{"value":{"description":{"value":"In app purchase: Belajar Inggeris AS"},"name":{"value":"Belajar Inggeris AS"},"localeCode":"ms"}},{"value":{"description":{"value":"In app purchase: Inglés estadounidense"},"name":{"value":"Inglés estadounidense"},"localeCode":"es-MX"}},{"value":{"description":{"value":"In app purchase: Lär dig Amerikansk engelska"},"name":{"value":"Lär dig Amerikansk engelska"},"localeCode":"sv"}},{"value":{"description":{"value":"In app purchase: Belajar Inggris (A.S.)"},"name":{"value":"Belajar Inggris (A.S.)"},"localeCode":"id"}},{"value":{"description":{"value":"In app purchase: Lær Amerikansk engelsk"},"name":{"value":"Lær Amerikansk engelsk"},"localeCode":"da"}}]},"id":"99487019","reviewScreenshot":{"value":{"assetToken":"Purple115/v4/31/d0/98/31d098ea-0495-8c47-0aaa-9d74ae6c33fb/pr_source.png","sortOrder":0,"type":"SortedN41ScreenShot","size":242851,"width":640,"height":1136,"checksum":"70c81d73d2005020654a0921fbfc5f5d","url":"https://is5-ssl.mzstatic.com/image/thumb/Purple115/v4/31/d0/98/31d098ea-0495-8c47-0aaa-9d74ae6c33fb/pr_source.png/640x1136ss-80.png","thumbNailUrl":"https://is5-ssl.mzstatic.com/image/thumb/Purple115/v4/31/d0/98/31d098ea-0495-8c47-0aaa-9d74ae6c33fb/pr_source.png/340x340bb-80.png","originalFileName":"ftl_8325cb9b79e79ac37d78ab233cea767b_4a7404053782ec2d5661d82d0021d934.png"}}}],"missingRequiredPrivacyPolicyData":false,"missingRequiredFamilyDetail":false} 
DEBUG [09:13:04]: << PUT ra/apps/1085013031/iaps/1407845488: 500 {"data"=>nil, "messages"=>{"warn"=>nil, "info"=>nil, "error"=>["ITC.response.error.OPERATION_FAILED"]}, "statusCode"=>"ERROR"}
DEBUG [09:13:04]: Request was successful

I replaced the data with "*****" because there was too much of it to upload as a comment

max-ott commented 5 years ago

Very strange. And you can confirm that the exact same operation is doable in the interface over at App Store Connect? (i.e. the in app purchase is editable?) I currently don't have any to test, so it's hard to check.

hhoweson commented 5 years ago

I can confirm that, yes. The save command fails even if nothing is changed though. In addition to that the code appears to offer the functionality to edit the localisations.

hhoweson commented 5 years ago

It appears this is not the first time someone has run into this issue: https://github.com/fastlane/fastlane/issues/13501 https://github.com/fastlane/fastlane/issues/10743 https://github.com/fastlane/fastlane/issues/10216

hhoweson commented 5 years ago

Here is a comparison of what apple sends vs what spaceship sends: https://www.diffchecker.com/NFAqamM3

There appears to be a lot of differences however I shouldn't imagine it would take too long to fix..?

max-ott commented 5 years ago

More than happy to review your pull request. I believe the in-app purchase part was added quite some time ago and therefore might lack updates or maintenance over time.

hhoweson commented 5 years ago

I think I might give it a pass and attempt to develop an alternate solution in php, I'm not particularly proficient in ruby and don't have a lot of time to spend on this, hopefully what I have discovered here might be useful for someone else who wishes to give it a go (or maybe me at some point in the future). Thanks for your help!

hhoweson commented 5 years ago

Here is my version that I created in php:

<?php

$authCookie = getAuthCookie('johnDoe@icloud.com', 'password');

$appId = '123456789';

$iaps = getIaps($appId, $authCookie);

foreach($iaps as $iap){

    print $iap['referenceName']."\n";

    $iapId = $iap['adamId'];

    $data = getIapData($appId, $iapId, $authCookie);

    // Do what you want with the data here

    updateIapData($data, $appId, $iapId, $authCookie);

}

function getIaps($appId, $authCookie){

    $url = "https://appstoreconnect.apple.com/WebObjects/iTunesConnect.woa/ra/apps/$appId/iaps";

    $response = json_decode(request($url, [], [$authCookie]), true);

    $statusCode = $response['statusCode'];

    if($statusCode === 'SUCCESS'){

        return $response['data'];

    } else {

        return null;

    }

}

function getIapData($appId, $iapId, $authCookie){

    $url = "https://appstoreconnect.apple.com/WebObjects/iTunesConnect.woa/ra/apps/$appId/iaps/$iapId";

    $response = json_decode(request($url, [], [$authCookie]), true);

    $statusCode = $response['statusCode'];

    return ($statusCode === 'SUCCESS')? $response['data'] : null;

}

function updateIapData($data, $appId, $iapId, $authCookie){

    $url = "https://appstoreconnect.apple.com/WebObjects/iTunesConnect.woa/ra/apps/$appId/iaps/$iapId";

    $response = json_decode(request($url, $data, [$authCookie], "PUT"), true);

    $statusCode = $response['statusCode'];

    return ($statusCode === SUCCESS);

}

function getAuthCookie($accountName, $password){ // Get authentication cookie from itunes connect

    $authServiceKey = json_decode(request('https://olympus.itunes.apple.com/v1/app/config?hostname=itunesconnect.apple.com'), true)['authServiceKey'];

    $url = "https://idmsa.apple.com/appleauth/auth/signin";

    $payload = [
        "accountName" => $accountName,
        "password" => $password,
        "rememberMe" => true
    ];

    $headers = [
        "X-Apple-Widget-Key: $authServiceKey"
    ];

    $result = request($url, $payload, $headers, "POST", true);

    preg_match_all('/^Set-Cookie:\s*([^;]*)/mi', $result, $matches);
    $cookies = array();
    foreach($matches[1] as $item) {
        parse_str($item, $cookie);
        $cookies = array_merge($cookies, $cookie);
    }

    if(!isset($cookies['myacinfo'])){
        die('Authentication failed');
    }

    $authCookie = "Cookie: myacinfo=".$cookies['myacinfo'];
    return $authCookie;

}

function request($url, $payload = array(), $headers = array(), $requestType = "GET", $returnHeaders = false){ // Generic curl request function

    $headers[] = "Content-Type: application/json;charset=UTF-8";

    $ch = curl_init();

    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload, JSON_UNESCAPED_SLASHES));
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $requestType);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_ENCODING, 'UTF-8');
    if($returnHeaders){
        curl_setopt($ch, CURLOPT_HEADER, 1);
    }

    $result = curl_exec($ch);

    if (curl_errno($ch)) {
        echo 'Error:' . curl_error($ch);
    }

    curl_close ($ch);

    return $result;

}

?>
fastlane-bot commented 5 years ago

There hasn't been any activity on this issue recently. Due to the high number of incoming GitHub notifications, we have to clean some of the old issues, as many of them have already been resolved with the latest updates.

Please make sure to update to the latest fastlane version and check if that solves the issue. Let us know if that works for you by adding a comment :+1:

fastlane-bot commented 5 years ago

This issue will be auto-closed because there hasn't been any activity for a few months. Feel free to open a new one if you still experience this problem :+1: