app_store_server_api_client 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e0d6c54805549e9e61a26f3dc874d091722c548ecc57ed45b9086c295f9d02db
4
+ data.tar.gz: 0f545da8ddc8a22a83d1e9fa382968f45e90a82d9059835822e30d02c2debf28
5
+ SHA512:
6
+ metadata.gz: d478dae3696e4f0e8eda24c12f83a9ccdb4f7ffc2e3f5e95c8b0ee62ef664278384fbeba0f728044eab5f1adb8f1eb6ac71a79ae86710d66b59894c06c179f46
7
+ data.tar.gz: e2106ce7f0313203f26c807f1ee90aa998cce02f4cb7dce765695f8853cdfeb8d82f9d6e3a473f6cd8bcb4cac30709713450cb4dbd04b801b10213dab1cc2c34
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,13 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.3
3
+
4
+ Style/StringLiterals:
5
+ Enabled: true
6
+ EnforcedStyle: single_quotes
7
+
8
+ Style/StringLiteralsInInterpolation:
9
+ Enabled: true
10
+ EnforcedStyle: single_quotes
11
+
12
+ Layout/LineLength:
13
+ Max: 120
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2025-02-14
4
+
5
+ - Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 watanabe
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,163 @@
1
+ # App Store Server API Client
2
+
3
+ A Ruby client for
4
+ the [App Store Server API](https://developer.apple.com/documentation/appstoreserverapi).
5
+
6
+ ## Support API Endpoints
7
+
8
+ * [Get Transaction Info](https://developer.apple.com/documentation/appstoreserverapi/get-v1-transactions-_transactionid_)
9
+ * [Request a Test Notification](https://developer.apple.com/documentation/appstoreserverapi/post-v1-notifications-test)
10
+ * [Get Test Notification Status](https://developer.apple.com/documentation/appstoreserverapi/get-v1-notifications-test-_testnotificationtoken_)
11
+ * [Get Transaction History](https://developer.apple.com/documentation/appstoreserverapi/get-v2-history-_transactionid_)
12
+
13
+ ## Requirements
14
+
15
+ Ruby 3.3.0 or later.
16
+
17
+ ## Installation
18
+
19
+ add this line to your application's Gemfile:
20
+
21
+ ```Gemfile
22
+ gem 'app_store_server_api_client'
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ### Prerequisites
28
+
29
+ To use this, please obtain an API Key.
30
+ https://developer.apple.com/documentation/appstoreserverapi/creating-api-keys-to-authorize-api-requests
31
+
32
+ ### Configure
33
+
34
+ **In your Rails application, create a client configure**
35
+
36
+ ```yaml
37
+ # my_app/config/app_store_server.yml
38
+ default: &default
39
+ private_key: |
40
+ -----BEGIN PRIVATE KEY-----
41
+ ...
42
+ -----END PRIVATE KEY-----
43
+ key_id: Z1BT391B21
44
+ issuer_id: ef02153z-1290-3519-875e-237a15237e3c
45
+ bundle_id: com.myapp.app
46
+ environment: sandbox
47
+
48
+ development:
49
+ <<: *default
50
+
51
+ test:
52
+ <<: *default
53
+
54
+ production:
55
+ <<: *default
56
+ ```
57
+
58
+ ### load the configuration
59
+
60
+ ```ruby
61
+ config = Rails.application.config_for(:app_store_server)
62
+ client = AppStoreServerApi::Client.new(**config)
63
+ ```
64
+
65
+ ## API
66
+
67
+ ### Get Transaction Info
68
+
69
+ [Get Transaction Info](
70
+ https://developer.apple.com/documentation/appstoreserverapi/get-v1-transactions-_transactionid_)
71
+
72
+ Get information about a single transaction for your app.
73
+
74
+ ```ruby
75
+ transaction_id = '2000000847061981'
76
+ client.get_transaction_info(transaction_id)
77
+ =>
78
+ {
79
+ "transactionId" => "2000000847061981",
80
+ "originalTransactionId" => "2000000847061981",
81
+ "bundleId" => "com.myapp.app",
82
+ "productId" => "com.myapp.app.product",
83
+ "type" => "Consumable",
84
+ "purchaseDate" => 1738645560000,
85
+ "originalPurchaseDate" => 1738645560000,
86
+ "quantity" => 1,
87
+ ...
88
+ }
89
+ ```
90
+
91
+ ### Request a Test Notification
92
+
93
+ [Request a Test Notification](https://developer.apple.com/documentation/appstoreserverapi/post-v1-notifications-test)
94
+
95
+ Ask App Store Server Notifications to send a test notification to your server.
96
+
97
+ ```ruby
98
+ result = client.request_test_notification
99
+ #=> {"testNotificationToken"=>"9f90efb9-2f75-4dbe-990c-5d1fc89f4546_1739179413123"}
100
+ ```
101
+
102
+ ### Get Test Notification Status
103
+
104
+ [Get Test Notification Status](https://developer.apple.com/documentation/appstoreserverapi/get-v1-notifications-test-_testnotificationtoken_)
105
+
106
+ Check the status of the test App Store server notification sent to your server.
107
+
108
+ ```ruby
109
+ test_notification_token = client.request_test_notification['testNotificationToken']
110
+ result = client.get_test_notification_status(test_notification_token)
111
+ #=> {
112
+ # "signedPayload"=> "eyJhbGciOiJFUzI1NiIsIng1YyI6...",
113
+ # "firstSendAttemptResult"=>"SUCCESS",
114
+ # "sendAttempts"=>[{"attemptDate"=>1739179888814, "sendAttemptResult"=>"SUCCESS"}]
115
+ #}
116
+
117
+ signed_payload = AppStoreServerApi::Utils::Decoder.decode_jws!(result['signedPayload'])
118
+ # => {
119
+ # "notificationType"=>"TEST",
120
+ # "notificationUUID"=>"3838df56-31ab-4e2e-9535-e6e9377c4c77",
121
+ # "data"=>{"bundleId"=>"com.myapp.app", "environment"=>"Sandbox"},
122
+ # "version"=>"2.0",
123
+ # "signedDate"=>1739180480080
124
+ # }
125
+ ```
126
+
127
+ ### Get Transaction History
128
+
129
+ [Get Transaction History](https://developer.apple.com/documentation/appstoreserverapi/get-v2-history-_transactionid_)
130
+
131
+ Get a customer’s in-app purchase transaction history for your app.
132
+
133
+ ```ruby
134
+ data = client.get_transaction_history(transaction_id,
135
+ params: {
136
+ sort: "DESCENDING"
137
+ })
138
+
139
+ transactions = AppStoreServerApi::Utils::Decoder.decode_transactions(signed_transactions:
140
+ data["signedTransactions"])
141
+ ```
142
+
143
+ ## Error Handling
144
+
145
+ ```ruby
146
+
147
+ begin
148
+ # response success
149
+ transaction_info = client.get_transaction_info('invalid_transaction_id')
150
+ rescue AppStoreServerApi::Error => e
151
+ # response failure
152
+ # case of error:
153
+ # - http status 40x, 50x
154
+ # - json parse error
155
+ puts e.code # => Integer
156
+ puts e.message # => String
157
+ end
158
+ ```
159
+
160
+ ## License
161
+
162
+ The gem is available as open source under the terms of
163
+ the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+ require 'jwt'
3
+ require 'faraday'
4
+ require 'uri'
5
+ require 'json'
6
+ require 'openssl'
7
+
8
+ module AppStoreServerApi
9
+ class Client
10
+ attr_reader :environment, :issuer_id, :key_id, :private_key, :bundle_id
11
+
12
+ PAYLOAD_AUD = 'appstoreconnect-v1'
13
+ TOKEN_TYPE = 'JWT'
14
+ ENCODE_ALGORITHM = 'ES256'
15
+ ENVIRONMENTS = [:production, :sandbox].freeze
16
+ API_BASE_URLS = {
17
+ :production => 'https://api.storekit.itunes.apple.com',
18
+ :sandbox => 'https://api.storekit-sandbox.itunes.apple.com'
19
+ }.freeze
20
+
21
+ # initialize client
22
+ # @param private_key [String] p8 key
23
+ # @param key_id [String] Your private key ID from App Store Connect (Ex: 2X9R4HXF34)
24
+ # @param issuer_id [String] Your issuer ID from the Keys page in App Store Connect
25
+ # @param bundle_id [String] Your app’s bundle ID (Ex: “com.example.testbundleid”)
26
+ # @param environment [Symbol] :production or :sandbox
27
+ def initialize(private_key:, key_id:, issuer_id:, bundle_id:, environment: :production)
28
+ self.environment = environment.to_sym
29
+ @issuer_id = issuer_id
30
+ @key_id = key_id
31
+ @private_key = private_key
32
+ @bundle_id = bundle_id
33
+ @http_client = Utils::HttpClient.new
34
+ end
35
+
36
+ # set environment
37
+ # @param env [Symbol] :production or :sandbox
38
+ # @raise [ArgumentError] if env is not :production or :sandbox
39
+ def environment=(env)
40
+ unless ENVIRONMENTS.include?(env)
41
+ raise ArgumentError, 'environment must be :production or :sandbox'
42
+ end
43
+
44
+ @environment = env
45
+ end
46
+
47
+ # get information about a single transaction
48
+ # @see https://developer.apple.com/documentation/appstoreserverapi/get-v1-transactions-_transactionid_
49
+ # @param [String] transaction_id The identifier of a transaction
50
+ # @return [Hash] transaction info
51
+ def get_transaction_info(transaction_id)
52
+ path = "/inApps/v1/transactions/#{transaction_id}"
53
+ response = do_request(path)
54
+ json = JSON.parse(response.body)
55
+ payload, = Utils::Decoder.decode_jws!(json['signedTransactionInfo'])
56
+ payload
57
+ end
58
+
59
+ # Request a Test Notification
60
+ # @see https://developer.apple.com/documentation/appstoreserverapi/post-v1-notifications-test
61
+ # @return [Hash] test notification token info
62
+ def request_test_notification
63
+ path = '/inApps/v1/notifications/test'
64
+ response = do_request(path, method: :post, params: {}.to_json)
65
+ JSON.parse(response.body)
66
+ end
67
+
68
+ # Get Test Notification Status
69
+ # @see https://developer.apple.com/documentation/appstoreserverapi/get-v1-notifications-test-_testnotificationtoken_
70
+ def get_test_notification_status(test_notification_token)
71
+ path = "/inApps/v1/notifications/test/#{test_notification_token}"
72
+ response = do_request(path)
73
+ JSON.parse(response.body)
74
+ end
75
+
76
+ # Get Transaction History
77
+ # @see https://developer.apple.com/documentation/appstoreserverapi/get-v2-history-_transactionid_
78
+ # @param [String] transaction_id The identifier of a transaction
79
+ # @param [Hash] params request params
80
+ # @return [Hash] transaction history
81
+ def get_transaction_history(transaction_id, params: {})
82
+ path = "/inApps/v2/history/#{transaction_id}"
83
+ response = do_request(path, params: params)
84
+ JSON.parse(response.body)
85
+ end
86
+
87
+ # generate bearer token
88
+ # @param issued_at [Time] issued at
89
+ # @param expired_in [Integer] expired in seconds (max 3600)
90
+ # @return [String] bearer token
91
+ def generate_bearer_token(issued_at: Time.now, expired_in: 3600)
92
+ # expirations longer than 60 minutes will be rejected
93
+ if expired_in > 3600
94
+ raise ArgumentError, 'expired_in must be less than or equal to 3600'
95
+ end
96
+
97
+ headers = {
98
+ alg: ENCODE_ALGORITHM,
99
+ kid: key_id,
100
+ typ: TOKEN_TYPE,
101
+ }
102
+
103
+ payload = {
104
+ iss: issuer_id,
105
+ iat: issued_at.to_i,
106
+ exp: (issued_at + expired_in).to_i,
107
+ aud: PAYLOAD_AUD,
108
+ bid: bundle_id
109
+ }
110
+
111
+ JWT.encode(payload, OpenSSL::PKey::EC.new(private_key), ENCODE_ALGORITHM, headers)
112
+ end
113
+
114
+ def api_base_url
115
+ API_BASE_URLS[environment]
116
+ end
117
+
118
+ def base_request_headers(bearer_token)
119
+ {
120
+ 'Content-Type' => 'application/json',
121
+ 'Authorization' => "Bearer #{bearer_token}"
122
+ }
123
+ end
124
+
125
+ # send get request to App Store Server API
126
+ # @param [String] path request path
127
+ # @param [Symbol] method request method
128
+ # @param [Hash,String,nil] params request params
129
+ # @param [Hash] headers additional headers
130
+ # @return [Faraday::Response] response
131
+ #
132
+ # @raise [Error::UnauthorizedError] if unauthorized error
133
+ # @raise [Error::ServerError] if server error
134
+ # @raise [Error] if other error
135
+ def do_request(path, method: :get, params: {}, headers: {}, open_timeout: 10, read_timeout: 30)
136
+ request_url = api_base_url + path
137
+ bearer_token = generate_bearer_token
138
+ request_headers = base_request_headers(bearer_token).merge(headers)
139
+
140
+ response = @http_client.request_with_retry(
141
+ url: request_url,
142
+ method: method,
143
+ params: params,
144
+ headers: request_headers)
145
+
146
+ if response.success?
147
+ return response
148
+ end
149
+
150
+ Error.handle_error(response)
151
+ end
152
+
153
+ end
154
+
155
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+ module AppStoreServerApi
3
+
4
+ class Error < StandardError
5
+ attr_reader :code, :response
6
+
7
+ # initialize error
8
+ # @param [Integer] code error code
9
+ # @param [String] message error message
10
+ # @param [Faraday::Response] response error response
11
+ def initialize(code:, message:, response:)
12
+ super(message)
13
+ @code = code
14
+ @response = response
15
+ end
16
+
17
+ def to_h
18
+ {
19
+ code: code,
20
+ message: message,
21
+ response: response
22
+ }
23
+ end
24
+
25
+ def inspect
26
+ "#<#{self.class.name}: #{to_h.to_json}>"
27
+ end
28
+
29
+ # The JSON Web Token (JWT) in the authorization header is invalid.
30
+ # For more information, see Generating JSON Web Tokens for API requests.
31
+ # @see https://developer.apple.com/documentation/appstoreserverapi/generating-json-web-tokens-for-api-requests
32
+ # other:
33
+ # - wrong environment (sandbox/production)
34
+ class UnauthorizedError < Error
35
+ def initialize(code: 4010000, message: 'unauthorized error', response:)
36
+ super(code: code, message: message, response: response)
37
+ end
38
+ end
39
+
40
+ class ServerError < Error
41
+ def initialize(code: 5000000, message: 'Internal Server Error', response:)
42
+ super(code: code, message: message, response: response)
43
+ end
44
+ end
45
+
46
+ # error response body is invalid
47
+ # must have errorCode and errorMessage.
48
+ # valid example response body:
49
+ # {
50
+ # "errorCode": 4000006,
51
+ # "errorMessage": "Invalid transaction id."
52
+ # }
53
+ class InvalidResponseError < Error
54
+ def initialize(code: 5000002, message: 'response body is invalid', response:)
55
+ super(code: code, message: message, response: response)
56
+ end
57
+ end
58
+
59
+ class TransactionIdNotFoundError < Error; end
60
+
61
+ class InvalidTransactionIdError < Error; end
62
+
63
+ class RateLimitExceededError < Error; end
64
+
65
+ class ServerNotificationURLNotFoundError < Error; end
66
+
67
+ class InvalidTestNotificationTokenError < Error; end
68
+
69
+ class TestNotificationNotFoundError < Error; end
70
+
71
+ # map error code to error class
72
+ ERROR_CODE_MAP = {
73
+ 4040010 => Error::TransactionIdNotFoundError,
74
+ 4000020 => Error::InvalidTestNotificationTokenError,
75
+ 4000006 => Error::InvalidTransactionIdError,
76
+ 4290000 => Error::RateLimitExceededError,
77
+ 4040007 => Error::ServerNotificationURLNotFoundError,
78
+ 4040008 => Error::TestNotificationNotFoundError,
79
+ }.freeze
80
+
81
+ # raise error from response
82
+ # @param [Faraday::Response] response error response
83
+ def self.handle_error(response)
84
+ case response.status
85
+ when 401
86
+ # Unauthorized error
87
+ # reasons:
88
+ # - JWT in the authorization header is invalid.
89
+ raise Error::UnauthorizedError.new(response: response)
90
+ when 500
91
+ raise Error::ServerError.new(response: response)
92
+ else
93
+ data = JSON.parse(response.body)
94
+
95
+ # error object must be {errorCode: Integer, errorMessage: String}
96
+ unless data.has_key?('errorCode') && data.has_key?('errorMessage')
97
+ raise Error::InvalidResponseError.new(message: 'response body is invalid', response: response)
98
+ end
99
+
100
+ error_code = data['errorCode']
101
+ error_class = ERROR_CODE_MAP[error_code] || Error
102
+ raise error_class.new(code: error_code, message: data['errorMessage'], response: response)
103
+ end
104
+ end
105
+
106
+ end
107
+
108
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+ require 'openssl'
3
+ require 'jwt'
4
+
5
+ module AppStoreServerApi
6
+ module Utils
7
+ module Decoder
8
+ module_function
9
+
10
+ # Decode a signed JWT
11
+ # @param [String] jws The signed JWT to decode
12
+ # @return [Hash] The decoded payload
13
+ def decode_jws!(jws)
14
+ apple_cert_store = make_apple_cert_store
15
+
16
+ payload, = JWT.decode(jws, nil, true, {algorithm: 'ES256'}) do |headers|
17
+ # verify the certificate included in the header x5c
18
+ cert_target, *cert_chain = headers['x5c'].map {|cert| OpenSSL::X509::Certificate.new(Base64.decode64(cert))}
19
+ apple_cert_store.verify(cert_target, cert_chain)
20
+ cert_target.public_key
21
+ end
22
+
23
+ payload
24
+ end
25
+
26
+ def decode_transaction(signed_transaction:)
27
+ decode_jws! signed_transaction
28
+ end
29
+
30
+ def decode_transactions(signed_transactions:)
31
+ signed_transactions.map do |signed_transaction|
32
+ decode_transaction signed_transaction: signed_transaction
33
+ end
34
+ end
35
+
36
+ def apple_root_certs
37
+ Dir.glob(File.join(__dir__, 'certs', '*.cer')).map do |filename|
38
+ OpenSSL::X509::Certificate.new File.read(filename)
39
+ end
40
+ end
41
+
42
+ def make_apple_cert_store
43
+ apple_cert_store = OpenSSL::X509::Store.new
44
+ apple_root_certs.each do |cert|
45
+ apple_cert_store.add_cert cert
46
+ end
47
+
48
+ apple_cert_store
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+ require 'faraday'
3
+ require 'retriable'
4
+
5
+ module AppStoreServerApi
6
+
7
+ module Utils
8
+
9
+ class HttpClient
10
+ DEFAULT_OPEN_TIMEOUT = 10
11
+ DEFAULT_READ_TIMEOUT = 30
12
+ RETRY_ERRORS = [Faraday::TimeoutError, Faraday::ConnectionFailed, Faraday::ServerError]
13
+
14
+ # initialize client
15
+ # @param open_timeout [Integer] open timeout
16
+ # @param read_timeout [Integer] read timeout
17
+ def initialize(open_timeout: DEFAULT_OPEN_TIMEOUT, read_timeout: DEFAULT_READ_TIMEOUT)
18
+ @open_timeout = open_timeout
19
+ @read_timeout = read_timeout
20
+ end
21
+
22
+ def build_connection
23
+ Faraday.new do |f|
24
+ f.adapter :net_http do |http|
25
+ http.open_timeout = @open_timeout
26
+ http.read_timeout = @read_timeout
27
+ end
28
+ end
29
+ end
30
+
31
+ def connection
32
+ @connection ||= build_connection
33
+ end
34
+
35
+ # send request
36
+ # @param url [String] request url
37
+ # @param method [Symbol] request method(:get, :post, :put, :patch, :delete)
38
+ # @param params [Hash,String,nil] request params
39
+ # @param headers [Hash] request headers
40
+ # @return [Faraday::Response]
41
+ def request(url:, method: :get, params: {}, headers: {})
42
+ method = method.to_sym
43
+
44
+ case method
45
+ when :get, :delete
46
+ connection.run_request(method, url, nil, headers) do |req|
47
+ req.params.update(params) if params.is_a?(Hash)
48
+ end
49
+ when :post, :put, :patch
50
+ connection.run_request(method, url, params, headers)
51
+ else
52
+ raise ArgumentError, "Unsupported HTTP method: #{method}"
53
+ end
54
+ end
55
+
56
+ # send request with retry
57
+ # @param url [String] request url
58
+ # @param method [Symbol] request method
59
+ # @param params [Hash,String,nil] request params
60
+ # @param headers [Hash] request headers
61
+ # @param retries [Integer] retry count
62
+ # @param base_interval [Float] base interval
63
+ # @param multiplier [Float] multiplier
64
+ # @param max_interval [Float] max interval
65
+ # @return [Faraday::Response]
66
+ def request_with_retry(url:, method: :get, params: {}, headers: {}, retries: 3,
67
+ base_interval: 0.5, multiplier: 1.0, max_interval: 30)
68
+
69
+ Retriable.retriable tries: retries,
70
+ base_interval: base_interval,
71
+ max_interval: max_interval,
72
+ multiplier: multiplier,
73
+ on: RETRY_ERRORS do
74
+ request(url: url, method: method, params: params, headers: headers)
75
+ end
76
+ end
77
+
78
+ end
79
+
80
+ end
81
+
82
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ module AppStoreServerApi
3
+ VERSION = '0.1.0'
4
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'app_store_server_api/version'
4
+ require_relative 'app_store_server_api/utils/decoder'
5
+ require_relative 'app_store_server_api/utils/http_client'
6
+ require_relative 'app_store_server_api/error'
7
+ require_relative 'app_store_server_api/client'
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'app_store_server_api/version'
4
+ require_relative 'app_store_server_api/utils/decoder'
5
+ require_relative 'app_store_server_api/utils/http_client'
6
+ require_relative 'app_store_server_api/error'
7
+ require_relative 'app_store_server_api/client'
@@ -0,0 +1,4 @@
1
+ module AppStoreServerApiClient
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: app_store_server_api_client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - mingos
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 2025-02-14 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: jwt
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '2.8'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '2.8'
26
+ - !ruby/object:Gem::Dependency
27
+ name: faraday
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.12'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.12'
40
+ - !ruby/object:Gem::Dependency
41
+ name: retriable
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '3.1'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '3.1'
54
+ description: Manage your customers' App Store transactions from your server.
55
+ email:
56
+ - mingos@pumb.jp
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - ".rspec"
62
+ - ".rubocop.yml"
63
+ - CHANGELOG.md
64
+ - LICENSE.txt
65
+ - README.md
66
+ - Rakefile
67
+ - lib/app_store_server_api/client.rb
68
+ - lib/app_store_server_api/error.rb
69
+ - lib/app_store_server_api/utils/certs/AppleRootCA-G3.cer
70
+ - lib/app_store_server_api/utils/decoder.rb
71
+ - lib/app_store_server_api/utils/http_client.rb
72
+ - lib/app_store_server_api/version.rb
73
+ - lib/app_store_server_api_client.rb
74
+ - lib/app_store_server_api_client.rb~
75
+ - sig/app_store_server_api_client.rbs
76
+ homepage: https://github.com/mingos/app-store-server-api-client
77
+ licenses:
78
+ - MIT
79
+ metadata:
80
+ homepage_uri: https://github.com/mingos/app-store-server-api-client
81
+ source_code_uri: https://github.com/mingos/app-store-server-api-client
82
+ changelog_uri: https://github.com/mingos/app-store-server-api-client/CHANGELOG.md
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: 3.3.0
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubygems_version: 3.6.2
98
+ specification_version: 4
99
+ summary: A Ruby client for App Store Server API
100
+ test_files: []