diff --git a/CHANGELOG.md b/CHANGELOG.md index a84fc5b..8a1ca55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ - Add Sandbox Messages API - Add Sending Domains API +- Add Billing API ## [2.6.0] - 2026-01-27 - Add Inboxes API diff --git a/README.md b/README.md index a9b3bdf..f87f2e6 100644 --- a/README.md +++ b/README.md @@ -191,6 +191,7 @@ Contact management: General API: - Templates CRUD – [`email_templates_api.rb`](examples/email_templates_api.rb) +- Billing – [`billing_api.rb`](examples/billing_api.rb) - Action Mailer – [`action_mailer.rb`](examples/action_mailer.rb) ## Migration guide v1 → v2 diff --git a/examples/billing_api.rb b/examples/billing_api.rb new file mode 100644 index 0000000..4862eef --- /dev/null +++ b/examples/billing_api.rb @@ -0,0 +1,9 @@ +require 'mailtrap' + +account_id = 3229 +client = Mailtrap::Client.new(api_key: 'your-api-key') +billing_api = Mailtrap::BillingAPI.new(account_id, client) + +# Get billing information for the account +billing_api.get +# => # diff --git a/lib/mailtrap.rb b/lib/mailtrap.rb index 50c19ef..d8cbee1 100644 --- a/lib/mailtrap.rb +++ b/lib/mailtrap.rb @@ -14,6 +14,7 @@ require_relative 'mailtrap/projects_api' require_relative 'mailtrap/inboxes_api' require_relative 'mailtrap/sandbox_messages_api' +require_relative 'mailtrap/billing_api' module Mailtrap # @!macro api_errors diff --git a/lib/mailtrap/billing.rb b/lib/mailtrap/billing.rb new file mode 100644 index 0000000..92c5ae9 --- /dev/null +++ b/lib/mailtrap/billing.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Mailtrap + # Data Transfer Object for Billing data + # @see https://docs.mailtrap.io/developers/account-management/billing + # @attr_reader billing [Hash] The billing cycles + # @attr_reader testing [Hash] Testing subscription details + # @attr_reader sending [Hash] Sending subscription details + # + Billing = Struct.new( + :billing, + :testing, + :sending, + keyword_init: true + ) +end diff --git a/lib/mailtrap/billing_api.rb b/lib/mailtrap/billing_api.rb new file mode 100644 index 0000000..a34521b --- /dev/null +++ b/lib/mailtrap/billing_api.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require_relative 'base_api' +require_relative 'billing' + +module Mailtrap + class BillingAPI + include BaseAPI + + self.response_class = Billing + + # Show billing details for the account + # @return Billing data for account + # @!macro api_errors + def get + response = client.get(base_path) + handle_response(response) + end + + private + + def base_path + "/api/accounts/#{account_id}/billing/usage" + end + end +end diff --git a/lib/mailtrap/project.rb b/lib/mailtrap/project.rb index aed082a..f8a6a36 100644 --- a/lib/mailtrap/project.rb +++ b/lib/mailtrap/project.rb @@ -2,7 +2,7 @@ module Mailtrap # Data Transfer Object for Project - # @see https://api-docs.mailtrap.io/docs/mailtrap-api-docs/ee252e413d78a-create-project + # @see https://docs.mailtrap.io/developers/email-sandbox/projects # @attr_reader id [Integer] The project ID # @attr_reader name [String] The project name # @attr_reader share_links [Hash] Admin and viewer share links diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_BillingAPI/_get/maps_response_data_to_Billing_object.yml b/spec/fixtures/vcr_cassettes/Mailtrap_BillingAPI/_get/maps_response_data_to_Billing_object.yml new file mode 100644 index 0000000..d9c3958 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_BillingAPI/_get/maps_response_data_to_Billing_object.yml @@ -0,0 +1,74 @@ +--- +http_interactions: +- request: + method: get + uri: https://mailtrap.io/api/accounts/1111111/billing/usage + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 200 + message: OK + headers: + Date: + - Tue, 17 Feb 2026 12:56:11 GMT + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Etag: + - W/"fcfb7d10033fb8e579fa7eed2e69857d" + Cache-Control: + - max-age=0, private, must-revalidate + X-Runtime: + - '0.032663' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: ASCII-8BIT + string: '{"billing":{"cycle_start":"2026-02-06T12:59:54.000Z","cycle_end":"2026-03-06T12:59:54.000Z"},"testing":{"plan":{"name":"Team"},"usage":{"sent_messages_count":{"current":0,"limit":5000},"forwarded_messages_count":{"current":0,"limit":500}}},"sending":{"plan":{"name":"Basic + 10K"},"usage":{"sent_messages_count":{"current":0,"limit":10000}}}}' + recorded_at: Tue, 17 Feb 2026 12:56:11 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/fixtures/vcr_cassettes/Mailtrap_BillingAPI/_get/when_api_key_is_incorrect/raises_authorization_error.yml b/spec/fixtures/vcr_cassettes/Mailtrap_BillingAPI/_get/when_api_key_is_incorrect/raises_authorization_error.yml new file mode 100644 index 0000000..3deb73d --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Mailtrap_BillingAPI/_get/when_api_key_is_incorrect/raises_authorization_error.yml @@ -0,0 +1,73 @@ +--- +http_interactions: +- request: + method: get + uri: https://mailtrap.io/api/accounts/1111111/billing/usage + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - mailtrap-ruby (https://github.com/mailtrap/mailtrap-ruby) + Host: + - mailtrap.io + Authorization: + - Bearer + Content-Type: + - application/json + response: + status: + code: 401 + message: Unauthorized + headers: + Date: + - Tue, 17 Feb 2026 12:56:12 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '31' + Connection: + - keep-alive + Server: + - cloudflare + X-Frame-Options: + - SAMEORIGIN + X-Xss-Protection: + - 1; mode=block + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Permitted-Cross-Domain-Policies: + - none + Referrer-Policy: + - strict-origin-when-cross-origin + Www-Authenticate: + - Token realm="Application" + Vary: + - Accept + X-Mailtrap-Version: + - v2 + X-Ratelimit-Limit: + - '150' + X-Ratelimit-Remaining: + - '149' + Cache-Control: + - no-cache + X-Runtime: + - '0.006363' + Strict-Transport-Security: + - max-age=0 + Cf-Cache-Status: + - DYNAMIC + Alt-Svc: + - h3=":443"; ma=86400 + body: + encoding: UTF-8 + string: '{"error":"Incorrect API token"}' + recorded_at: Tue, 17 Feb 2026 12:56:12 GMT +recorded_with: VCR 6.4.0 diff --git a/spec/mailtrap/billing_api_spec.rb b/spec/mailtrap/billing_api_spec.rb new file mode 100644 index 0000000..95c070f --- /dev/null +++ b/spec/mailtrap/billing_api_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +RSpec.describe Mailtrap::BillingAPI, :vcr do + subject(:billing_api) { described_class.new(account_id, client) } + + let(:account_id) { ENV.fetch('MAILTRAP_ACCOUNT_ID', 1_111_111) } + let(:client) { Mailtrap::Client.new(api_key: ENV.fetch('MAILTRAP_API_KEY', 'local-api-key')) } + + describe '#get' do + subject(:get) { billing_api.get } + + it 'maps response data to Billing object' do + expect(get).to be_a(Mailtrap::Billing) + + expect(get).to match_struct( + billing: { cycle_start: '2026-02-06T12:59:54.000Z', cycle_end: '2026-03-06T12:59:54.000Z' }, + testing: { plan: { name: 'Team' }, + usage: { sent_messages_count: { current: 0, limit: 5000 }, + forwarded_messages_count: { current: 0, limit: 500 } } }, + sending: { plan: { name: 'Basic 10K' }, usage: { sent_messages_count: { current: 0, limit: 10_000 } } } + ) + end + + context 'when api key is incorrect' do + let(:client) { Mailtrap::Client.new(api_key: 'incorrect-api-key') } + + it 'raises authorization error' do + expect { get }.to raise_error do |error| + expect(error).to be_a(Mailtrap::AuthorizationError) + expect(error.message).to include('Incorrect API token') + expect(error.messages.any? { |msg| msg.include?('Incorrect API token') }).to be true + end + end + end + end +end diff --git a/spec/mailtrap/billing_spec.rb b/spec/mailtrap/billing_spec.rb new file mode 100644 index 0000000..b2c2d06 --- /dev/null +++ b/spec/mailtrap/billing_spec.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +RSpec.describe Mailtrap::Billing do + describe '#initialize' do + subject(:billing) { described_class.new(attributes) } + + let(:attributes) do + { + billing: { + cycle_start: '2024-02-15T21:11:59.624Z', + cycle_end: '2024-02-15T21:11:59.624Z' + }, + testing: { + plan: { + name: 'Individual' + }, + usage: { + sent_messages_count: { + current: 1234, + limit: 5000 + }, + forwarded_messages_count: { + current: 0, + limit: 100 + } + } + }, + sending: { + plan: { + name: 'Basic 10K' + }, + usage: { + sent_messages_count: { + current: 6789, + limit: 10_000 + } + } + } + } + end + + it 'creates a billing data with all attributes' do + expect(billing).to match_struct( + billing: { + cycle_start: '2024-02-15T21:11:59.624Z', + cycle_end: '2024-02-15T21:11:59.624Z' + }, + testing: { + plan: { + name: 'Individual' + }, + usage: { + sent_messages_count: { + current: 1234, + limit: 5000 + }, + forwarded_messages_count: { + current: 0, + limit: 100 + } + } + }, + sending: { + plan: { + name: 'Basic 10K' + }, + usage: { + sent_messages_count: { + current: 6789, + limit: 10_000 + } + } + } + ) + end + end +end