I am building a script to upload new apps to store via fastlane. However, fastlane currently faces issues with setting prices for newly created apps.SO I created ruby scripts to do it manually via api.
First thing I did was to to get price point of my app with help from this appstore documentation. I was able to get the price point for $0USD and I wanted to set it as the price of my app. I used a patch request to this endpoint, however I am unable to do so.
Heres an update of my scripts, If anyone can point me to the right direction.
lane :setAppPrice do
auth_token = authAppstoreAPI()
puts "Token: #{auth_token}"
app_id = 'app_id_here'
price_point_id = get_app_price_points(auth_token: auth_token, app_id: app_id)
if price_point_id
url = URI("https://api.appstoreconnect.apple.com/v1/#{app_id}/appPricePoints/#{price_point_id}")
# Create the HTTP request for updating the app price
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
# Prepare the PATCH request
headers = {
"Authorization" => "Bearer #{auth_token}",
"Content-Type" => "application/json"
}
request = Net::HTTP::Patch.new(url, headers)
# Prepare the body with the new price data
request.body = {
data: {
id: price_point_id,
type: "appPricePoints",
attributes: {
customerPrice: 0.0 # Set your desired new price here
}
}
}.to_json
# Execute the request
response = http.request(request)
# Handle the response
if response.code == "200"
UI.success("Successfully updated app price to 0.0 for price point ID #{price_point_id}")
else
UI.error("Failed to update app price: #{response.body}")
end
end
end
lane :get_app_price_points do |options|
require 'json'
require 'net/http'
auth_token = options[:auth_token] # Your App Store Connect API token
app_id = options[:app_id] # The app ID for which you want to list subscriptions
# Define the URL for fetching subscriptions
url = URI("https://api.appstoreconnect.apple.com/v1/apps/#{app_id}/appPricePoints?filter%5Bterritory%5D=USA,CAN&include=territory")
# Create the HTTP request
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
# Set up headers for the App Store Connect API request
headers = {
"Authorization" => "Bearer #{auth_token}",
"Content-Type" => "application/json"
}
# Prepare the GET request
request = Net::HTTP::Get.new(url, headers)
# Execute the request
response = http.request(request)
# Handle the response
if response.code == "200"
app_prices = JSON.parse(response.body)
# UI.message("Subscriptions for app #{app_id}: #{response.body}")
# Use the find method to get the free price point ID directly
free_price_point = app_prices["data"].find do |price_point|
price_point["attributes"]["customerPrice"] == "0.0"
end
free_price_point_id = free_price_point ? free_price_point["id"] : nil
UI.message("Free Price Point ID: #{free_price_point_id}")
# Return the ID of the price point with 0.0 price
free_price_point_id
else
UI.error("Failed to fetch subscriptions: #{response.body}")
end
end
Okay finally I was able to set prices on the apps after scouring and big help from the apple developer forums here.
I created custom fastlane lanes to do all of it. I basically set free price ($0.00) for 2 territories (USA and CAN). Feel free to modify these for your needs.
Here are the custom fastlane lanes.
Main lane to set the price:
lane :setAppPrice do
auth_token = authAppstoreAPI() #your appstore api token
puts "Token: #{auth_token}"
app_id = 'your_app_id_here'
#
# Have to get a price point ID for each territory.
#
# We had to individually get the price point ID for each territory
# even though we can merge it into one request. This is because
# when setting prices through appPriceSchedules, api response with
# a 'baseTerritory must have a single manual price'.
#
# These is very helpful if you need to set prices for different territories.
#
# Note: This is only to set prices to 0.0 for all
# territories.
#
territories= ["USA", "CAN"] # Add more territories if needed
territories.each do |territory|
price_point_id = get_app_price_points(auth_token: auth_token, app_id: app_id, territory_id: territory)
setAppPriceToZero(auth_token: auth_token, app_id: app_id, territory_id: territory, price_point_id: price_point_id)
end
end
The lane to set price:
lane :setAppPriceToZero do |options|
require 'json'
require 'net/http'
auth_token = options[:auth_token]
app_id = options[:app_id]
territory_id = options[:territory_id]
price_point_id = options[:price_point_id]
UI.user_error!("Missing parameters: auth_token, app_id, price_point_id and territory_id") unless auth_token && app_id && territory_id && price_point_id
UI.message "Setting app price to 0.0 for app #{app_id} in territory #{territory_id} with price point ID #{price_point_id}"
url = URI("https://api.appstoreconnect.apple.com/v1/appPriceSchedules")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
headers = {
"Authorization" => "Bearer #{auth_token}",
"Content-Type" => "application/json"
}
request = Net::HTTP::Post.new(url, headers)
payload = {
"data": {
"type": "appPriceSchedules",
"relationships": {
"app": {
"data": {
"type": "apps",
"id": app_id
}
},
"baseTerritory": {
"data": {
"type": "territories",
"id": territory_id
}
},
"manualPrices": {
"data": [
{
"id": "manualPrice-0",
"type": "appPrices"
}
]
}
}
},
"included": [
{
"id": "manualPrice-0",
"type": "appPrices",
#
# Uncomment the following lines if you want to set a start and end date
#
# "attributes": {
# "startDate": "NONE",
# "endDate": "NONE"
# },
"relationships": {
"appPricePoint": {
"data": {
"type": "appPricePoints",
"id": price_point_id
}
}
}
}
]
}
request.body = payload.to_json
response = http.request(request)
UI.message "Response: #{response.body} & #{response.code}"
end
Then the lane to get free($0.0) price_point_id:
#
# Get free price point for a territory
#
lane :get_app_price_points do |options|
require 'json'
require 'net/http'
auth_token = options[:auth_token] # Your App Store Connect API token
app_id = options[:app_id] # The app ID for which you want to list subscriptions
territory_id = options[:territory_id] # The territory ID for which you want to list subscriptions
# all params are required
UI.user_error!("Missing parameters: auth_token, app_id and territory_id") unless auth_token && app_id && territory_id
# if need be you can combine multiple territories in one request,
# example: territory%5D=USA,CAN,GBR
url = URI("https://api.appstoreconnect.apple.com/v1/apps/#{app_id}/appPricePoints?filter%5Bterritory%5D=#{territory_id}&include=territory")
# Create the HTTP request
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
# Set up headers for the App Store Connect API request
headers = {
"Authorization" => "Bearer #{auth_token}",
"Content-Type" => "application/json"
}
# Prepare the GET request
request = Net::HTTP::Get.new(url, headers)
# Execute the request
response = http.request(request)
# Handle the response
if response.code == "200"
app_prices = JSON.parse(response.body)
# Use the find method to get the free price point ID directly
free_price_point = app_prices["data"].find do |price_point|
price_point["attributes"]["customerPrice"] == "0.0"
end
free_price_point_id = free_price_point ? free_price_point["id"] : nil
UI.message("Free Price Point ID for #{territory_id}: #{free_price_point_id}")
# Return the ID of the price point with 0.0 price
free_price_point_id
else
UI.error("Failed to fetch subscriptions: #{response.body}")
end
end
EDIT You can combine it with your existing workflows. For example, after you run the produce(...) action lane you can get the app id and then just pass it to setAppPrice lane:
produce(....)
app_identifier = lane_context[SharedValues::PRODUCE_APPLE_ID]
setAppPrice(app_id: app_identifier)