Class: AppStoreServerApi::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/app_store_server_api/client.rb

Constant Summary collapse

PAYLOAD_AUD =
'appstoreconnect-v1'
TOKEN_TYPE =
'JWT'
ENCODE_ALGORITHM =
'ES256'
ENVIRONMENTS =
[:production, :sandbox].freeze
API_BASE_URLS =
{
  :production => 'https://api.storekit.itunes.apple.com',
  :sandbox => 'https://api.storekit-sandbox.itunes.apple.com'
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(private_key:, key_id:, issuer_id:, bundle_id:, environment: :production) ⇒ Client

initialize client

Parameters:

  • private_key (String)

    p8 key

  • key_id (String)

    Your private key ID from App Store Connect (Ex: 2X9R4HXF34)

  • issuer_id (String)

    Your issuer ID from the Keys page in App Store Connect

  • bundle_id (String)

    Your app’s bundle ID (Ex: “com.example.testbundleid”)

  • environment (Symbol) (defaults to: :production)

    :production or :sandbox



28
29
30
31
32
33
34
35
# File 'lib/app_store_server_api/client.rb', line 28

def initialize(private_key:, key_id:, issuer_id:, bundle_id:, environment: :production)
  self.environment = environment.to_sym
  @issuer_id = issuer_id
  @key_id = key_id
  @private_key = private_key
  @bundle_id = bundle_id
  @http_client = Utils::HttpClient.new
end

Instance Attribute Details

#bundle_idObject (readonly)

Returns the value of attribute bundle_id.



11
12
13
# File 'lib/app_store_server_api/client.rb', line 11

def bundle_id
  @bundle_id
end

#environmentObject

Returns the value of attribute environment.



11
12
13
# File 'lib/app_store_server_api/client.rb', line 11

def environment
  @environment
end

#issuer_idObject (readonly)

Returns the value of attribute issuer_id.



11
12
13
# File 'lib/app_store_server_api/client.rb', line 11

def issuer_id
  @issuer_id
end

#key_idObject (readonly)

Returns the value of attribute key_id.



11
12
13
# File 'lib/app_store_server_api/client.rb', line 11

def key_id
  @key_id
end

#private_keyObject (readonly)

Returns the value of attribute private_key.



11
12
13
# File 'lib/app_store_server_api/client.rb', line 11

def private_key
  @private_key
end

Instance Method Details

#api_base_urlObject



124
125
126
# File 'lib/app_store_server_api/client.rb', line 124

def api_base_url
  API_BASE_URLS[environment]
end

#base_request_headers(bearer_token) ⇒ Object



128
129
130
131
132
133
# File 'lib/app_store_server_api/client.rb', line 128

def base_request_headers(bearer_token)
  {
    'Content-Type' => 'application/json',
    'Authorization' => "Bearer #{bearer_token}"
  }
end

#do_request(path, method: :get, params: {}, headers: {}, open_timeout: 10, read_timeout: 30) ⇒ Faraday::Response

send get request to App Store Server API

Parameters:

  • path (String)

    request path

  • method (Symbol) (defaults to: :get)

    request method

  • params (Hash, String, nil) (defaults to: {})

    request params

  • headers (Hash) (defaults to: {})

    additional headers

Returns:

  • (Faraday::Response)

    response

Raises:



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/app_store_server_api/client.rb', line 145

def do_request(path, method: :get, params: {}, headers: {}, open_timeout: 10, read_timeout: 30)
  request_url = api_base_url + path
  bearer_token = generate_bearer_token
  request_headers = base_request_headers(bearer_token).merge(headers)

  response = @http_client.request_with_retry(
    url: request_url,
    method: method,
    params: params,
    headers: request_headers)

  if response.success?
    return response
  end

  Error.handle_error(response)
end

#generate_bearer_token(issued_at: Time.now, expired_in: 3600) ⇒ String

generate bearer token

Parameters:

  • issued_at (Time) (defaults to: Time.now)

    issued at

  • expired_in (Integer) (defaults to: 3600)

    expired in seconds (max 3600)

Returns:

  • (String)

    bearer token



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/app_store_server_api/client.rb', line 101

def generate_bearer_token(issued_at: Time.now, expired_in: 3600)
  # expirations longer than 60 minutes will be rejected
  if expired_in > 3600
    raise ArgumentError, 'expired_in must be less than or equal to 3600'
  end

  headers = {
    alg: ENCODE_ALGORITHM,
    kid: key_id,
    typ: TOKEN_TYPE,
  }

  payload = {
    iss: issuer_id,
    iat: issued_at.to_i,
    exp: (issued_at + expired_in).to_i,
    aud: PAYLOAD_AUD,
    bid: bundle_id
  }

  JWT.encode(payload, OpenSSL::PKey::EC.new(private_key), ENCODE_ALGORITHM, headers)
end

#get_all_subscription_statuses(transaction_id, params: nil) ⇒ Object

Get All Subscription Statuses

Parameters:

  • transaction_id (String)

    The identifier of a transaction

See Also:



91
92
93
94
95
# File 'lib/app_store_server_api/client.rb', line 91

def get_all_subscription_statuses(transaction_id, params: nil)
  path = "/inApps/v1/subscriptions/#{transaction_id}"
  response = do_request(path, params: params)
  JSON.parse(response.body)
end

#get_test_notification_status(test_notification_token) ⇒ Object

Get Test Notification Status



71
72
73
74
75
# File 'lib/app_store_server_api/client.rb', line 71

def get_test_notification_status(test_notification_token)
  path = "/inApps/v1/notifications/test/#{test_notification_token}"
  response = do_request(path)
  JSON.parse(response.body)
end

#get_transaction_history(transaction_id, params: nil) ⇒ Hash

Get Transaction History

Parameters:

  • transaction_id (String)

    The identifier of a transaction

  • params (Hash) (defaults to: nil)

    request params

Returns:

  • (Hash)

    transaction history

See Also:



82
83
84
85
86
# File 'lib/app_store_server_api/client.rb', line 82

def get_transaction_history(transaction_id, params: nil)
  path = "/inApps/v2/history/#{transaction_id}"
  response = do_request(path, params: params)
  JSON.parse(response.body)
end

#get_transaction_info(transaction_id) ⇒ Hash

get information about a single transaction

Parameters:

  • transaction_id (String)

    The identifier of a transaction

Returns:

  • (Hash)

    transaction info

See Also:



52
53
54
55
56
57
58
# File 'lib/app_store_server_api/client.rb', line 52

def get_transaction_info(transaction_id)
  path = "/inApps/v1/transactions/#{transaction_id}"
  response = do_request(path)
  json = JSON.parse(response.body)
  payload, = Utils::Decoder.decode_jws!(json['signedTransactionInfo'])
  payload
end

#request_test_notificationHash

Request a Test Notification

Returns:

  • (Hash)

    test notification token info

See Also:



63
64
65
66
67
# File 'lib/app_store_server_api/client.rb', line 63

def request_test_notification
  path = '/inApps/v1/notifications/test'
  response = do_request(path, method: :post, params: {}.to_json)
  JSON.parse(response.body)
end