diff --git a/packages/stripe_api/lib/src/models/models.dart b/packages/stripe_api/lib/src/models/models.dart index b5636972..95e2fd0a 100644 --- a/packages/stripe_api/lib/src/models/models.dart +++ b/packages/stripe_api/lib/src/models/models.dart @@ -1,3 +1,4 @@ +export 'stripe_billing_meter.dart'; export 'stripe_checkout_session.dart'; export 'stripe_customer.dart'; export 'stripe_event.dart'; diff --git a/packages/stripe_api/lib/src/models/stripe_billing_meter.dart b/packages/stripe_api/lib/src/models/stripe_billing_meter.dart new file mode 100644 index 00000000..cb833d88 --- /dev/null +++ b/packages/stripe_api/lib/src/models/stripe_billing_meter.dart @@ -0,0 +1,32 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'stripe_billing_meter.g.dart'; + +/// {@template stripe_billing_meter} +/// A billing meter in Stripe. +/// +/// See https://docs.stripe.com/api/billing/meter/object +/// {@endtemplate} +@JsonSerializable(createToJson: false) +class StripeBillingMeter { + /// {@macro stripe_billing_meter} + StripeBillingMeter({ + required this.id, + required this.displayName, + required this.eventName, + }); + + /// Converts a JSON object to a [StripeBillingMeter]. + factory StripeBillingMeter.fromJson(Map json) => + _$StripeBillingMeterFromJson(json); + + /// The unique identifier for this object. + final String id; + + /// The meter's name. + final String displayName; + + /// The name of the meter event to record usage for. Corresponds with the + /// event_name field on meter events. + final String eventName; +} diff --git a/packages/stripe_api/lib/src/models/stripe_billing_meter.g.dart b/packages/stripe_api/lib/src/models/stripe_billing_meter.g.dart new file mode 100644 index 00000000..50fadffc --- /dev/null +++ b/packages/stripe_api/lib/src/models/stripe_billing_meter.g.dart @@ -0,0 +1,27 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ignore_for_file: implicit_dynamic_parameter, require_trailing_commas, cast_nullable_to_non_nullable, lines_longer_than_80_chars, unnecessary_lambdas + +part of 'stripe_billing_meter.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +StripeBillingMeter _$StripeBillingMeterFromJson(Map json) => + $checkedCreate( + 'StripeBillingMeter', + json, + ($checkedConvert) { + final val = StripeBillingMeter( + id: $checkedConvert('id', (v) => v as String), + displayName: $checkedConvert('display_name', (v) => v as String), + eventName: $checkedConvert('event_name', (v) => v as String), + ); + return val; + }, + fieldKeyMap: const { + 'displayName': 'display_name', + 'eventName': 'event_name', + }, + ); diff --git a/packages/stripe_api/lib/src/stripe_api.dart b/packages/stripe_api/lib/src/stripe_api.dart index b2835b38..31c540ea 100644 --- a/packages/stripe_api/lib/src/stripe_api.dart +++ b/packages/stripe_api/lib/src/stripe_api.dart @@ -65,6 +65,16 @@ class StripeApi { ); } + /// Retrieves all [StripeBillingMeter]s associated with the Stripe account. + Future> fetchActiveBillingMeters() async { + return _fetchAllPages( + path: 'billing/meters', + queryParameters: {'status': 'active'}, + fromJson: StripeBillingMeter.fromJson, + getId: (e) => e.id, + ); + } + /// Creates a new meter event for the given [customerId]. /// See https://docs.stripe.com/api/billing/meter-event Future createMeterEvent({ diff --git a/packages/stripe_api/test/fixtures/stripe/json/billing_meters_page.json b/packages/stripe_api/test/fixtures/stripe/json/billing_meters_page.json new file mode 100644 index 00000000..b91e9379 --- /dev/null +++ b/packages/stripe_api/test/fixtures/stripe/json/billing_meters_page.json @@ -0,0 +1,31 @@ +{ + "object": "list", + "data": [ + { + "id": "mtr_test_61QvSUDTnLya5cdwG41HSA9cXarIc144", + "object": "billing.meter", + "created": 1723254937, + "customer_mapping": { + "event_payload_key": "stripe_customer_id", + "type": "by_id" + }, + "default_aggregation": { + "formula": "sum" + }, + "display_name": "Patch Installs", + "event_name": "patch_installs", + "event_time_window": null, + "livemode": false, + "status": "active", + "status_transitions": { + "deactivated_at": null + }, + "updated": 1723254937, + "value_settings": { + "event_payload_key": "value" + } + } + ], + "has_more": true, + "url": "/v1/billing/meters" +} diff --git a/packages/stripe_api/test/fixtures/stripe/stripe_fixtures.dart b/packages/stripe_api/test/fixtures/stripe/stripe_fixtures.dart index 6b63881a..e1bcc923 100644 --- a/packages/stripe_api/test/fixtures/stripe/stripe_fixtures.dart +++ b/packages/stripe_api/test/fixtures/stripe/stripe_fixtures.dart @@ -3,6 +3,13 @@ import 'dart:io'; const stripeJsonPath = 'test/fixtures/stripe/json'; +String get billingMetersPageJsonString => File( + '$stripeJsonPath/billing_meters_page.json', +).readAsStringSync(); + +Map get billingMetersPageJson => + jsonDecode(billingMetersPageJsonString) as Map; + String get checkoutSessionCompletedEventJsonString => File( '$stripeJsonPath/checkout_session_completed_event.json', ).readAsStringSync(); diff --git a/packages/stripe_api/test/src/stripe_api_test.dart b/packages/stripe_api/test/src/stripe_api_test.dart index ef016b0b..c80fab2e 100644 --- a/packages/stripe_api/test/src/stripe_api_test.dart +++ b/packages/stripe_api/test/src/stripe_api_test.dart @@ -1,3 +1,4 @@ +import 'dart:convert'; import 'dart:io'; import 'package:http/http.dart' as http; @@ -215,6 +216,53 @@ void main() { ); }); + group('fetchBillingMeters', () { + const meterId = 'mtr_test_61QvSUDTnLya5cdwG41HSA9cXarIc144'; + setUp(() { + when( + () => httpClient.get( + Uri.parse( + 'https://api.stripe.com/v1/billing/meters?status=active&limit=100', + ), + headers: any(named: 'headers'), + ), + ).thenAnswer( + (_) async => http.Response( + billingMetersPageJsonString, + HttpStatus.ok, + ), + ); + + when( + () => httpClient.get( + Uri.parse( + 'https://api.stripe.com/v1/billing/meters?status=active&limit=100&starting_after=$meterId', + ), + headers: any(named: 'headers'), + ), + ).thenAnswer( + (_) async => http.Response( + jsonEncode({ + 'object': 'list', + 'data': >[], + 'has_more': false, + }), + HttpStatus.ok, + ), + ); + }); + + test('returns a billing meter on successful request', () async { + final billingMeters = await stripeApi.fetchActiveBillingMeters(); + expect(billingMeters, hasLength(1)); + + final billingMeter = billingMeters.first; + expect(billingMeter.id, meterId); + expect(billingMeter.displayName, 'Patch Installs'); + expect(billingMeter.eventName, 'patch_installs'); + }); + }); + group('createMeterEvent', () { final uri = Uri.parse('https://api.stripe.com/v1/billing/meter_events');