diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 5153ca9..97e4d74 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.49.0" + ".": "1.50.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 8f4293e..df8db45 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 118 +configured_endpoints: 126 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/orb%2Forb-a5a28a58483355d3cc3da7ac5c452d548ee17183324318198052968121ca7dba.yml openapi_spec_hash: a317931a99e6d4a122919135a0363e40 -config_hash: 05c94c0e6dbeab2c9b554c2e0d6371a0 +config_hash: bcf82bddb691f6be773ac6cae8c03b9a diff --git a/CHANGELOG.md b/CHANGELOG.md index e47afe0..16d9c9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.50.0 (2026-01-08) + +Full Changelog: [v1.49.0...v1.50.0](https://github.com/orbcorp/orb-go/compare/v1.49.0...v1.50.0) + +### Features + +* **api:** manual updates ([619d599](https://github.com/orbcorp/orb-go/commit/619d5991d058fbad50a8db80355a0015965d25ce)) + ## 1.49.0 (2026-01-06) Full Changelog: [v1.48.0...v1.49.0](https://github.com/orbcorp/orb-go/compare/v1.48.0...v1.49.0) diff --git a/README.md b/README.md index 45260ae..9c3aa4a 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Or to pin the version: ```sh -go get -u 'github.com/orbcorp/orb-go@v1.49.0' +go get -u 'github.com/orbcorp/orb-go@v1.50.0' ``` diff --git a/api.md b/api.md index dcbb2b3..3c24a59 100644 --- a/api.md +++ b/api.md @@ -381,15 +381,18 @@ Methods: Response Types: - orb.InvoiceFetchUpcomingResponse +- orb.InvoiceListSummaryResponse Methods: - client.Invoices.New(ctx context.Context, body orb.InvoiceNewParams) (shared.Invoice, error) - client.Invoices.Update(ctx context.Context, invoiceID string, body orb.InvoiceUpdateParams) (shared.Invoice, error) - client.Invoices.List(ctx context.Context, query orb.InvoiceListParams) (pagination.Page[shared.Invoice], error) +- client.Invoices.DeleteLineItem(ctx context.Context, invoiceID string, lineItemID string) error - client.Invoices.Fetch(ctx context.Context, invoiceID string) (shared.Invoice, error) - client.Invoices.FetchUpcoming(ctx context.Context, query orb.InvoiceFetchUpcomingParams) (orb.InvoiceFetchUpcomingResponse, error) - client.Invoices.Issue(ctx context.Context, invoiceID string, body orb.InvoiceIssueParams) (shared.Invoice, error) +- client.Invoices.ListSummary(ctx context.Context, query orb.InvoiceListSummaryParams) (pagination.Page[orb.InvoiceListSummaryResponse], error) - client.Invoices.MarkPaid(ctx context.Context, invoiceID string, body orb.InvoiceMarkPaidParams) (shared.Invoice, error) - client.Invoices.Pay(ctx context.Context, invoiceID string) (shared.Invoice, error) - client.Invoices.Void(ctx context.Context, invoiceID string) (shared.Invoice, error) @@ -441,6 +444,20 @@ Methods: - client.Plans.ExternalPlanID.Update(ctx context.Context, otherExternalPlanID string, body orb.PlanExternalPlanIDUpdateParams) (orb.Plan, error) - client.Plans.ExternalPlanID.Fetch(ctx context.Context, externalPlanID string) (orb.Plan, error) +## Migrations + +Response Types: + +- orb.PlanMigrationGetResponse +- orb.PlanMigrationListResponse +- orb.PlanMigrationCancelResponse + +Methods: + +- client.Plans.Migrations.Get(ctx context.Context, planID string, migrationID string) (orb.PlanMigrationGetResponse, error) +- client.Plans.Migrations.List(ctx context.Context, planID string, query orb.PlanMigrationListParams) (pagination.Page[orb.PlanMigrationListResponse], error) +- client.Plans.Migrations.Cancel(ctx context.Context, planID string, migrationID string) (orb.PlanMigrationCancelResponse, error) + # Prices Response Types: @@ -581,11 +598,24 @@ Response Types: - orb.MutatedSubscription - orb.SubscriptionChangeGetResponse +- orb.SubscriptionChangeListResponse - orb.SubscriptionChangeApplyResponse - orb.SubscriptionChangeCancelResponse Methods: - client.SubscriptionChanges.Get(ctx context.Context, subscriptionChangeID string) (orb.SubscriptionChangeGetResponse, error) +- client.SubscriptionChanges.List(ctx context.Context, query orb.SubscriptionChangeListParams) (pagination.Page[orb.SubscriptionChangeListResponse], error) - client.SubscriptionChanges.Apply(ctx context.Context, subscriptionChangeID string, body orb.SubscriptionChangeApplyParams) (orb.SubscriptionChangeApplyResponse, error) - client.SubscriptionChanges.Cancel(ctx context.Context, subscriptionChangeID string) (orb.SubscriptionChangeCancelResponse, error) + +# CreditBlocks + +Response Types: + +- orb.CreditBlockGetResponse + +Methods: + +- client.CreditBlocks.Get(ctx context.Context, blockID string) (orb.CreditBlockGetResponse, error) +- client.CreditBlocks.Delete(ctx context.Context, blockID string) error diff --git a/client.go b/client.go index 637dd95..a41e714 100644 --- a/client.go +++ b/client.go @@ -35,6 +35,7 @@ type Client struct { Webhooks *WebhookService SubscriptionChanges *SubscriptionChangeService + CreditBlocks *CreditBlockService } // DefaultClientOptions read from the environment (ORB_API_KEY, ORB_WEBHOOK_SECRET, @@ -79,6 +80,7 @@ func NewClient(opts ...option.RequestOption) (r *Client) { r.Alerts = NewAlertService(opts...) r.DimensionalPriceGroups = NewDimensionalPriceGroupService(opts...) r.SubscriptionChanges = NewSubscriptionChangeService(opts...) + r.CreditBlocks = NewCreditBlockService(opts...) return } diff --git a/creditblock.go b/creditblock.go new file mode 100644 index 0000000..3b7cf95 --- /dev/null +++ b/creditblock.go @@ -0,0 +1,188 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package orb + +import ( + "context" + "errors" + "fmt" + "net/http" + "slices" + "time" + + "github.com/orbcorp/orb-go/internal/apijson" + "github.com/orbcorp/orb-go/internal/requestconfig" + "github.com/orbcorp/orb-go/option" +) + +// CreditBlockService contains methods and other services that help with +// interacting with the orb API. +// +// Note, unlike clients, this service does not read variables from the environment +// automatically. You should not instantiate this service directly, and instead use +// the [NewCreditBlockService] method instead. +type CreditBlockService struct { + Options []option.RequestOption +} + +// NewCreditBlockService generates a new service that applies the given options to +// each request. These options are applied after the parent client's options (if +// there is one), and before any request-specific options. +func NewCreditBlockService(opts ...option.RequestOption) (r *CreditBlockService) { + r = &CreditBlockService{} + r.Options = opts + return +} + +// This endpoint returns a credit block identified by its block_id. +func (r *CreditBlockService) Get(ctx context.Context, blockID string, opts ...option.RequestOption) (res *CreditBlockGetResponse, err error) { + opts = slices.Concat(r.Options, opts) + if blockID == "" { + err = errors.New("missing required block_id parameter") + return + } + path := fmt.Sprintf("credit_blocks/%s", blockID) + err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, nil, &res, opts...) + return +} + +// This endpoint deletes a credit block by its ID. +// +// When a credit block is deleted: +// +// - The block is removed from the customer's credit ledger. +// - Any usage of the credit block is reversed, and the ledger is replayed as if +// the block never existed. +// - If invoices were generated from the purchase of the credit block, they will be +// deleted if in draft status, voided if issued, or a credit note will be issued +// if the invoice is paid. +// +// +// Issued invoices that had credits applied from this block will not be regenerated, but the ledger will +// reflect the state as if credits from the deleted block were never applied. +// +func (r *CreditBlockService) Delete(ctx context.Context, blockID string, opts ...option.RequestOption) (err error) { + opts = slices.Concat(r.Options, opts) + opts = append([]option.RequestOption{option.WithHeader("Accept", "*/*")}, opts...) + if blockID == "" { + err = errors.New("missing required block_id parameter") + return + } + path := fmt.Sprintf("credit_blocks/%s", blockID) + err = requestconfig.ExecuteNewRequest(ctx, http.MethodDelete, path, nil, nil, opts...) + return +} + +// The Credit Block resource models prepaid credits within Orb. +type CreditBlockGetResponse struct { + ID string `json:"id,required"` + Balance float64 `json:"balance,required"` + EffectiveDate time.Time `json:"effective_date,required,nullable" format:"date-time"` + ExpiryDate time.Time `json:"expiry_date,required,nullable" format:"date-time"` + Filters []CreditBlockGetResponseFilter `json:"filters,required"` + MaximumInitialBalance float64 `json:"maximum_initial_balance,required,nullable"` + PerUnitCostBasis string `json:"per_unit_cost_basis,required,nullable"` + Status CreditBlockGetResponseStatus `json:"status,required"` + JSON creditBlockGetResponseJSON `json:"-"` +} + +// creditBlockGetResponseJSON contains the JSON metadata for the struct +// [CreditBlockGetResponse] +type creditBlockGetResponseJSON struct { + ID apijson.Field + Balance apijson.Field + EffectiveDate apijson.Field + ExpiryDate apijson.Field + Filters apijson.Field + MaximumInitialBalance apijson.Field + PerUnitCostBasis apijson.Field + Status apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *CreditBlockGetResponse) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r creditBlockGetResponseJSON) RawJSON() string { + return r.raw +} + +type CreditBlockGetResponseFilter struct { + // The property of the price to filter on. + Field CreditBlockGetResponseFiltersField `json:"field,required"` + // Should prices that match the filter be included or excluded. + Operator CreditBlockGetResponseFiltersOperator `json:"operator,required"` + // The IDs or values that match this filter. + Values []string `json:"values,required"` + JSON creditBlockGetResponseFilterJSON `json:"-"` +} + +// creditBlockGetResponseFilterJSON contains the JSON metadata for the struct +// [CreditBlockGetResponseFilter] +type creditBlockGetResponseFilterJSON struct { + Field apijson.Field + Operator apijson.Field + Values apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *CreditBlockGetResponseFilter) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r creditBlockGetResponseFilterJSON) RawJSON() string { + return r.raw +} + +// The property of the price to filter on. +type CreditBlockGetResponseFiltersField string + +const ( + CreditBlockGetResponseFiltersFieldPriceID CreditBlockGetResponseFiltersField = "price_id" + CreditBlockGetResponseFiltersFieldItemID CreditBlockGetResponseFiltersField = "item_id" + CreditBlockGetResponseFiltersFieldPriceType CreditBlockGetResponseFiltersField = "price_type" + CreditBlockGetResponseFiltersFieldCurrency CreditBlockGetResponseFiltersField = "currency" + CreditBlockGetResponseFiltersFieldPricingUnitID CreditBlockGetResponseFiltersField = "pricing_unit_id" +) + +func (r CreditBlockGetResponseFiltersField) IsKnown() bool { + switch r { + case CreditBlockGetResponseFiltersFieldPriceID, CreditBlockGetResponseFiltersFieldItemID, CreditBlockGetResponseFiltersFieldPriceType, CreditBlockGetResponseFiltersFieldCurrency, CreditBlockGetResponseFiltersFieldPricingUnitID: + return true + } + return false +} + +// Should prices that match the filter be included or excluded. +type CreditBlockGetResponseFiltersOperator string + +const ( + CreditBlockGetResponseFiltersOperatorIncludes CreditBlockGetResponseFiltersOperator = "includes" + CreditBlockGetResponseFiltersOperatorExcludes CreditBlockGetResponseFiltersOperator = "excludes" +) + +func (r CreditBlockGetResponseFiltersOperator) IsKnown() bool { + switch r { + case CreditBlockGetResponseFiltersOperatorIncludes, CreditBlockGetResponseFiltersOperatorExcludes: + return true + } + return false +} + +type CreditBlockGetResponseStatus string + +const ( + CreditBlockGetResponseStatusActive CreditBlockGetResponseStatus = "active" + CreditBlockGetResponseStatusPendingPayment CreditBlockGetResponseStatus = "pending_payment" +) + +func (r CreditBlockGetResponseStatus) IsKnown() bool { + switch r { + case CreditBlockGetResponseStatusActive, CreditBlockGetResponseStatusPendingPayment: + return true + } + return false +} diff --git a/creditblock_test.go b/creditblock_test.go new file mode 100644 index 0000000..adc7574 --- /dev/null +++ b/creditblock_test.go @@ -0,0 +1,58 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package orb_test + +import ( + "context" + "errors" + "os" + "testing" + + "github.com/orbcorp/orb-go" + "github.com/orbcorp/orb-go/internal/testutil" + "github.com/orbcorp/orb-go/option" +) + +func TestCreditBlockGet(t *testing.T) { + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := orb.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + _, err := client.CreditBlocks.Get(context.TODO(), "block_id") + if err != nil { + var apierr *orb.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} + +func TestCreditBlockDelete(t *testing.T) { + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := orb.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + err := client.CreditBlocks.Delete(context.TODO(), "block_id") + if err != nil { + var apierr *orb.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} diff --git a/internal/version.go b/internal/version.go index f335c92..00a06b7 100644 --- a/internal/version.go +++ b/internal/version.go @@ -2,4 +2,4 @@ package internal -const PackageVersion = "1.49.0" // x-release-please-version +const PackageVersion = "1.50.0" // x-release-please-version diff --git a/invoice.go b/invoice.go index d5d8e4d..bf67220 100644 --- a/invoice.go +++ b/invoice.go @@ -114,6 +114,27 @@ func (r *InvoiceService) ListAutoPaging(ctx context.Context, query InvoiceListPa return pagination.NewPageAutoPager(r.List(ctx, query, opts...)) } +// This endpoint deletes an invoice line item from a draft invoice. +// +// This endpoint only allows deletion of one-off line items (not subscription-based +// line items). The invoice must be in a draft status for this operation to +// succeed. +func (r *InvoiceService) DeleteLineItem(ctx context.Context, invoiceID string, lineItemID string, opts ...option.RequestOption) (err error) { + opts = slices.Concat(r.Options, opts) + opts = append([]option.RequestOption{option.WithHeader("Accept", "*/*")}, opts...) + if invoiceID == "" { + err = errors.New("missing required invoice_id parameter") + return + } + if lineItemID == "" { + err = errors.New("missing required line_item_id parameter") + return + } + path := fmt.Sprintf("invoices/%s/invoice_line_items/%s", invoiceID, lineItemID) + err = requestconfig.ExecuteNewRequest(ctx, http.MethodDelete, path, nil, nil, opts...) + return +} + // This endpoint is used to fetch an [`Invoice`](/core-concepts#invoice) given an // identifier. func (r *InvoiceService) Fetch(ctx context.Context, invoiceID string, opts ...option.RequestOption) (res *shared.Invoice, err error) { @@ -154,6 +175,59 @@ func (r *InvoiceService) Issue(ctx context.Context, invoiceID string, body Invoi return } +// This is a lighter-weight endpoint that returns a list of all +// [`Invoice`](/core-concepts#invoice) summaries for an account in a list format. +// +// These invoice summaries do not include line item details, minimums, maximums, +// and discounts, making this endpoint more efficient. +// +// The list of invoices is ordered starting from the most recently issued invoice +// date. The response also includes +// [`pagination_metadata`](/api-reference/pagination), which lets the caller +// retrieve the next page of results if they exist. +// +// By default, this only returns invoices that are `issued`, `paid`, or `synced`. +// +// When fetching any `draft` invoices, this returns the last-computed invoice +// values for each draft invoice, which may not always be up-to-date since Orb +// regularly refreshes invoices asynchronously. +func (r *InvoiceService) ListSummary(ctx context.Context, query InvoiceListSummaryParams, opts ...option.RequestOption) (res *pagination.Page[InvoiceListSummaryResponse], err error) { + var raw *http.Response + opts = slices.Concat(r.Options, opts) + opts = append([]option.RequestOption{option.WithResponseInto(&raw)}, opts...) + path := "invoices/summary" + cfg, err := requestconfig.NewRequestConfig(ctx, http.MethodGet, path, query, &res, opts...) + if err != nil { + return nil, err + } + err = cfg.Execute() + if err != nil { + return nil, err + } + res.SetPageConfig(cfg, raw) + return res, nil +} + +// This is a lighter-weight endpoint that returns a list of all +// [`Invoice`](/core-concepts#invoice) summaries for an account in a list format. +// +// These invoice summaries do not include line item details, minimums, maximums, +// and discounts, making this endpoint more efficient. +// +// The list of invoices is ordered starting from the most recently issued invoice +// date. The response also includes +// [`pagination_metadata`](/api-reference/pagination), which lets the caller +// retrieve the next page of results if they exist. +// +// By default, this only returns invoices that are `issued`, `paid`, or `synced`. +// +// When fetching any `draft` invoices, this returns the last-computed invoice +// values for each draft invoice, which may not always be up-to-date since Orb +// regularly refreshes invoices asynchronously. +func (r *InvoiceService) ListSummaryAutoPaging(ctx context.Context, query InvoiceListSummaryParams, opts ...option.RequestOption) *pagination.PageAutoPager[InvoiceListSummaryResponse] { + return pagination.NewPageAutoPager(r.ListSummary(ctx, query, opts...)) +} + // This endpoint allows an invoice's status to be set to the `paid` status. This // can only be done to invoices that are in the `issued` or `synced` status. func (r *InvoiceService) MarkPaid(ctx context.Context, invoiceID string, body InvoiceMarkPaidParams, opts ...option.RequestOption) (res *shared.Invoice, err error) { @@ -1078,197 +1152,791 @@ func (r InvoiceFetchUpcomingResponseStatus) IsKnown() bool { return false } -type InvoiceNewParams struct { - // An ISO 4217 currency string. Must be the same as the customer's currency if it - // is set. - Currency param.Field[string] `json:"currency,required"` - // Optional invoice date to set. Must be in the past, if not set, `invoice_date` is - // set to the current time in the customer's timezone. - InvoiceDate param.Field[time.Time] `json:"invoice_date,required" format:"date-time"` - LineItems param.Field[[]InvoiceNewParamsLineItem] `json:"line_items,required"` - // The id of the `Customer` to create this invoice for. One of `customer_id` and - // `external_customer_id` are required. - CustomerID param.Field[string] `json:"customer_id"` - // An optional discount to attach to the invoice. - Discount param.Field[shared.DiscountUnionParam] `json:"discount"` - // An optional custom due date for the invoice. If not set, the due date will be - // calculated based on the `net_terms` value. - DueDate param.Field[time.Time] `json:"due_date" format:"date-time"` - // The `external_customer_id` of the `Customer` to create this invoice for. One of - // `customer_id` and `external_customer_id` are required. - ExternalCustomerID param.Field[string] `json:"external_customer_id"` - // An optional memo to attach to the invoice. If no memo is provided, we will - // attach the default memo - Memo param.Field[string] `json:"memo"` - // User-specified key/value pairs for the resource. Individual keys can be removed - // by setting the value to `null`, and the entire metadata mapping can be cleared - // by setting `metadata` to `null`. - Metadata param.Field[map[string]string] `json:"metadata"` - // The net terms determines the due date of the invoice. Due date is calculated - // based on the invoice or issuance date, depending on the account's configured due - // date calculation method. A value of '0' here represents that the invoice is due - // on issue, whereas a value of '30' represents that the customer has 30 days to - // pay the invoice. Do not set this field if you want to set a custom due date. - NetTerms param.Field[int64] `json:"net_terms"` - // When true, this invoice will be submitted for issuance upon creation. When - // false, the resulting invoice will require manual review to issue. Defaulted to - // false. - WillAutoIssue param.Field[bool] `json:"will_auto_issue"` -} - -func (r InvoiceNewParams) MarshalJSON() (data []byte, err error) { - return apijson.MarshalRoot(r) -} - -type InvoiceNewParamsLineItem struct { - // A date string to specify the line item's end date in the customer's timezone. - EndDate param.Field[time.Time] `json:"end_date,required" format:"date"` - ItemID param.Field[string] `json:"item_id,required"` - ModelType param.Field[InvoiceNewParamsLineItemsModelType] `json:"model_type,required"` - // The name of the line item. - Name param.Field[string] `json:"name,required"` - // The number of units on the line item - Quantity param.Field[float64] `json:"quantity,required"` - // A date string to specify the line item's start date in the customer's timezone. - StartDate param.Field[time.Time] `json:"start_date,required" format:"date"` - // Configuration for unit pricing - UnitConfig param.Field[shared.UnitConfigParam] `json:"unit_config,required"` -} - -func (r InvoiceNewParamsLineItem) MarshalJSON() (data []byte, err error) { - return apijson.MarshalRoot(r) -} - -type InvoiceNewParamsLineItemsModelType string - -const ( - InvoiceNewParamsLineItemsModelTypeUnit InvoiceNewParamsLineItemsModelType = "unit" -) - -func (r InvoiceNewParamsLineItemsModelType) IsKnown() bool { - switch r { - case InvoiceNewParamsLineItemsModelTypeUnit: - return true - } - return false -} - -type InvoiceUpdateParams struct { - // An optional custom due date for the invoice. If not set, the due date will be - // calculated based on the `net_terms` value. - DueDate param.Field[time.Time] `json:"due_date" format:"date-time"` - // The date of the invoice. Can only be modified for one-off draft invoices. - InvoiceDate param.Field[time.Time] `json:"invoice_date" format:"date-time"` - // User-specified key/value pairs for the resource. Individual keys can be removed - // by setting the value to `null`, and the entire metadata mapping can be cleared - // by setting `metadata` to `null`. - Metadata param.Field[map[string]string] `json:"metadata"` - // The net terms determines the due date of the invoice. Due date is calculated - // based on the invoice or issuance date, depending on the account's configured due - // date calculation method. A value of '0' here represents that the invoice is due - // on issue, whereas a value of '30' represents that the customer has 30 days to - // pay the invoice. Do not set this field if you want to set a custom due date. - NetTerms param.Field[int64] `json:"net_terms"` -} - -func (r InvoiceUpdateParams) MarshalJSON() (data []byte, err error) { - return apijson.MarshalRoot(r) -} - -type InvoiceListParams struct { - Amount param.Field[string] `query:"amount"` - AmountGt param.Field[string] `query:"amount[gt]"` - AmountLt param.Field[string] `query:"amount[lt]"` - // Cursor for pagination. This can be populated by the `next_cursor` value returned - // from the initial request. - Cursor param.Field[string] `query:"cursor"` - CustomerID param.Field[string] `query:"customer_id"` - DateType param.Field[InvoiceListParamsDateType] `query:"date_type"` - DueDate param.Field[time.Time] `query:"due_date" format:"date"` - // Filters invoices by their due dates within a specific time range in the past. - // Specify the range as a number followed by 'd' (days) or 'm' (months). For - // example, '7d' filters invoices due in the last 7 days, and '2m' filters those - // due in the last 2 months. - DueDateWindow param.Field[string] `query:"due_date_window"` - DueDateGt param.Field[time.Time] `query:"due_date[gt]" format:"date"` - DueDateLt param.Field[time.Time] `query:"due_date[lt]" format:"date"` - ExternalCustomerID param.Field[string] `query:"external_customer_id"` - InvoiceDateGt param.Field[time.Time] `query:"invoice_date[gt]" format:"date-time"` - InvoiceDateGte param.Field[time.Time] `query:"invoice_date[gte]" format:"date-time"` - InvoiceDateLt param.Field[time.Time] `query:"invoice_date[lt]" format:"date-time"` - InvoiceDateLte param.Field[time.Time] `query:"invoice_date[lte]" format:"date-time"` - IsRecurring param.Field[bool] `query:"is_recurring"` - // The number of items to fetch. Defaults to 20. - Limit param.Field[int64] `query:"limit"` - Status param.Field[[]InvoiceListParamsStatus] `query:"status"` - SubscriptionID param.Field[string] `query:"subscription_id"` -} - -// URLQuery serializes [InvoiceListParams]'s query parameters as `url.Values`. -func (r InvoiceListParams) URLQuery() (v url.Values) { - return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{ - ArrayFormat: apiquery.ArrayQueryFormatBrackets, - NestedFormat: apiquery.NestedQueryFormatBrackets, - }) -} - -type InvoiceListParamsDateType string - -const ( - InvoiceListParamsDateTypeDueDate InvoiceListParamsDateType = "due_date" - InvoiceListParamsDateTypeInvoiceDate InvoiceListParamsDateType = "invoice_date" -) - -func (r InvoiceListParamsDateType) IsKnown() bool { - switch r { - case InvoiceListParamsDateTypeDueDate, InvoiceListParamsDateTypeInvoiceDate: - return true - } - return false -} - -type InvoiceListParamsStatus string - -const ( - InvoiceListParamsStatusDraft InvoiceListParamsStatus = "draft" - InvoiceListParamsStatusIssued InvoiceListParamsStatus = "issued" - InvoiceListParamsStatusPaid InvoiceListParamsStatus = "paid" - InvoiceListParamsStatusSynced InvoiceListParamsStatus = "synced" - InvoiceListParamsStatusVoid InvoiceListParamsStatus = "void" -) - -func (r InvoiceListParamsStatus) IsKnown() bool { - switch r { - case InvoiceListParamsStatusDraft, InvoiceListParamsStatusIssued, InvoiceListParamsStatusPaid, InvoiceListParamsStatusSynced, InvoiceListParamsStatusVoid: - return true - } - return false -} - -type InvoiceFetchUpcomingParams struct { - SubscriptionID param.Field[string] `query:"subscription_id,required"` -} - -// URLQuery serializes [InvoiceFetchUpcomingParams]'s query parameters as -// `url.Values`. -func (r InvoiceFetchUpcomingParams) URLQuery() (v url.Values) { - return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{ - ArrayFormat: apiquery.ArrayQueryFormatBrackets, - NestedFormat: apiquery.NestedQueryFormatBrackets, - }) -} - -type InvoiceIssueParams struct { - // If true, the invoice will be issued synchronously. If false, the invoice will be - // issued asynchronously. The synchronous option is only available for invoices - // that have no usage fees. If the invoice is configured to sync to an external - // provider, a successful response from this endpoint guarantees the invoice is - // present in the provider. - Synchronous param.Field[bool] `json:"synchronous"` -} - -func (r InvoiceIssueParams) MarshalJSON() (data []byte, err error) { - return apijson.MarshalRoot(r) +// #InvoiceApiResourceWithoutLineItems +type InvoiceListSummaryResponse struct { + ID string `json:"id,required"` + // This is the final amount required to be charged to the customer and reflects the + // application of the customer balance to the `total` of the invoice. + AmountDue string `json:"amount_due,required"` + AutoCollection InvoiceListSummaryResponseAutoCollection `json:"auto_collection,required"` + BillingAddress shared.Address `json:"billing_address,required,nullable"` + // The creation time of the resource in Orb. + CreatedAt time.Time `json:"created_at,required" format:"date-time"` + // A list of credit notes associated with the invoice + CreditNotes []InvoiceListSummaryResponseCreditNote `json:"credit_notes,required"` + // An ISO 4217 currency string or `credits` + Currency string `json:"currency,required"` + Customer shared.CustomerMinified `json:"customer,required"` + CustomerBalanceTransactions []InvoiceListSummaryResponseCustomerBalanceTransaction `json:"customer_balance_transactions,required"` + // Tax IDs are commonly required to be displayed on customer invoices, which are + // added to the headers of invoices. + // + // ### Supported Tax ID Countries and Types + // + // | Country | Type | Description | + // | ---------------------- | ------------ | ------------------------------------------------------------------------------------------------------- | + // | Albania | `al_tin` | Albania Tax Identification Number | + // | Andorra | `ad_nrt` | Andorran NRT Number | + // | Angola | `ao_tin` | Angola Tax Identification Number | + // | Argentina | `ar_cuit` | Argentinian Tax ID Number | + // | Armenia | `am_tin` | Armenia Tax Identification Number | + // | Aruba | `aw_tin` | Aruba Tax Identification Number | + // | Australia | `au_abn` | Australian Business Number (AU ABN) | + // | Australia | `au_arn` | Australian Taxation Office Reference Number | + // | Austria | `eu_vat` | European VAT Number | + // | Azerbaijan | `az_tin` | Azerbaijan Tax Identification Number | + // | Bahamas | `bs_tin` | Bahamas Tax Identification Number | + // | Bahrain | `bh_vat` | Bahraini VAT Number | + // | Bangladesh | `bd_bin` | Bangladesh Business Identification Number | + // | Barbados | `bb_tin` | Barbados Tax Identification Number | + // | Belarus | `by_tin` | Belarus TIN Number | + // | Belgium | `eu_vat` | European VAT Number | + // | Benin | `bj_ifu` | Benin Tax Identification Number (Identifiant Fiscal Unique) | + // | Bolivia | `bo_tin` | Bolivian Tax ID | + // | Bosnia and Herzegovina | `ba_tin` | Bosnia and Herzegovina Tax Identification Number | + // | Brazil | `br_cnpj` | Brazilian CNPJ Number | + // | Brazil | `br_cpf` | Brazilian CPF Number | + // | Bulgaria | `bg_uic` | Bulgaria Unified Identification Code | + // | Bulgaria | `eu_vat` | European VAT Number | + // | Burkina Faso | `bf_ifu` | Burkina Faso Tax Identification Number (Numéro d'Identifiant Fiscal Unique) | + // | Cambodia | `kh_tin` | Cambodia Tax Identification Number | + // | Cameroon | `cm_niu` | Cameroon Tax Identification Number (Numéro d'Identifiant fiscal Unique) | + // | Canada | `ca_bn` | Canadian BN | + // | Canada | `ca_gst_hst` | Canadian GST/HST Number | + // | Canada | `ca_pst_bc` | Canadian PST Number (British Columbia) | + // | Canada | `ca_pst_mb` | Canadian PST Number (Manitoba) | + // | Canada | `ca_pst_sk` | Canadian PST Number (Saskatchewan) | + // | Canada | `ca_qst` | Canadian QST Number (Québec) | + // | Cape Verde | `cv_nif` | Cape Verde Tax Identification Number (Número de Identificação Fiscal) | + // | Chile | `cl_tin` | Chilean TIN | + // | China | `cn_tin` | Chinese Tax ID | + // | Colombia | `co_nit` | Colombian NIT Number | + // | Congo-Kinshasa | `cd_nif` | Congo (DR) Tax Identification Number (Número de Identificação Fiscal) | + // | Costa Rica | `cr_tin` | Costa Rican Tax ID | + // | Croatia | `eu_vat` | European VAT Number | + // | Croatia | `hr_oib` | Croatian Personal Identification Number (OIB) | + // | Cyprus | `eu_vat` | European VAT Number | + // | Czech Republic | `eu_vat` | European VAT Number | + // | Denmark | `eu_vat` | European VAT Number | + // | Dominican Republic | `do_rcn` | Dominican RCN Number | + // | Ecuador | `ec_ruc` | Ecuadorian RUC Number | + // | Egypt | `eg_tin` | Egyptian Tax Identification Number | + // | El Salvador | `sv_nit` | El Salvadorian NIT Number | + // | Estonia | `eu_vat` | European VAT Number | + // | Ethiopia | `et_tin` | Ethiopia Tax Identification Number | + // | European Union | `eu_oss_vat` | European One Stop Shop VAT Number for non-Union scheme | + // | Finland | `eu_vat` | European VAT Number | + // | France | `eu_vat` | European VAT Number | + // | Georgia | `ge_vat` | Georgian VAT | + // | Germany | `de_stn` | German Tax Number (Steuernummer) | + // | Germany | `eu_vat` | European VAT Number | + // | Greece | `eu_vat` | European VAT Number | + // | Guinea | `gn_nif` | Guinea Tax Identification Number (Número de Identificação Fiscal) | + // | Hong Kong | `hk_br` | Hong Kong BR Number | + // | Hungary | `eu_vat` | European VAT Number | + // | Hungary | `hu_tin` | Hungary Tax Number (adószám) | + // | Iceland | `is_vat` | Icelandic VAT | + // | India | `in_gst` | Indian GST Number | + // | Indonesia | `id_npwp` | Indonesian NPWP Number | + // | Ireland | `eu_vat` | European VAT Number | + // | Israel | `il_vat` | Israel VAT | + // | Italy | `eu_vat` | European VAT Number | + // | Japan | `jp_cn` | Japanese Corporate Number (_Hōjin Bangō_) | + // | Japan | `jp_rn` | Japanese Registered Foreign Businesses' Registration Number (_Tōroku Kokugai Jigyōsha no Tōroku Bangō_) | + // | Japan | `jp_trn` | Japanese Tax Registration Number (_Tōroku Bangō_) | + // | Kazakhstan | `kz_bin` | Kazakhstani Business Identification Number | + // | Kenya | `ke_pin` | Kenya Revenue Authority Personal Identification Number | + // | Kyrgyzstan | `kg_tin` | Kyrgyzstan Tax Identification Number | + // | Laos | `la_tin` | Laos Tax Identification Number | + // | Latvia | `eu_vat` | European VAT Number | + // | Liechtenstein | `li_uid` | Liechtensteinian UID Number | + // | Liechtenstein | `li_vat` | Liechtenstein VAT Number | + // | Lithuania | `eu_vat` | European VAT Number | + // | Luxembourg | `eu_vat` | European VAT Number | + // | Malaysia | `my_frp` | Malaysian FRP Number | + // | Malaysia | `my_itn` | Malaysian ITN | + // | Malaysia | `my_sst` | Malaysian SST Number | + // | Malta | `eu_vat` | European VAT Number | + // | Mauritania | `mr_nif` | Mauritania Tax Identification Number (Número de Identificação Fiscal) | + // | Mexico | `mx_rfc` | Mexican RFC Number | + // | Moldova | `md_vat` | Moldova VAT Number | + // | Montenegro | `me_pib` | Montenegro PIB Number | + // | Morocco | `ma_vat` | Morocco VAT Number | + // | Nepal | `np_pan` | Nepal PAN Number | + // | Netherlands | `eu_vat` | European VAT Number | + // | New Zealand | `nz_gst` | New Zealand GST Number | + // | Nigeria | `ng_tin` | Nigerian Tax Identification Number | + // | North Macedonia | `mk_vat` | North Macedonia VAT Number | + // | Northern Ireland | `eu_vat` | Northern Ireland VAT Number | + // | Norway | `no_vat` | Norwegian VAT Number | + // | Norway | `no_voec` | Norwegian VAT on e-commerce Number | + // | Oman | `om_vat` | Omani VAT Number | + // | Peru | `pe_ruc` | Peruvian RUC Number | + // | Philippines | `ph_tin` | Philippines Tax Identification Number | + // | Poland | `eu_vat` | European VAT Number | + // | Portugal | `eu_vat` | European VAT Number | + // | Romania | `eu_vat` | European VAT Number | + // | Romania | `ro_tin` | Romanian Tax ID Number | + // | Russia | `ru_inn` | Russian INN | + // | Russia | `ru_kpp` | Russian KPP | + // | Saudi Arabia | `sa_vat` | Saudi Arabia VAT | + // | Senegal | `sn_ninea` | Senegal NINEA Number | + // | Serbia | `rs_pib` | Serbian PIB Number | + // | Singapore | `sg_gst` | Singaporean GST | + // | Singapore | `sg_uen` | Singaporean UEN | + // | Slovakia | `eu_vat` | European VAT Number | + // | Slovenia | `eu_vat` | European VAT Number | + // | Slovenia | `si_tin` | Slovenia Tax Number (davčna številka) | + // | South Africa | `za_vat` | South African VAT Number | + // | South Korea | `kr_brn` | Korean BRN | + // | Spain | `es_cif` | Spanish NIF Number (previously Spanish CIF Number) | + // | Spain | `eu_vat` | European VAT Number | + // | Suriname | `sr_fin` | Suriname FIN Number | + // | Sweden | `eu_vat` | European VAT Number | + // | Switzerland | `ch_uid` | Switzerland UID Number | + // | Switzerland | `ch_vat` | Switzerland VAT Number | + // | Taiwan | `tw_vat` | Taiwanese VAT | + // | Tajikistan | `tj_tin` | Tajikistan Tax Identification Number | + // | Tanzania | `tz_vat` | Tanzania VAT Number | + // | Thailand | `th_vat` | Thai VAT | + // | Turkey | `tr_tin` | Turkish Tax Identification Number | + // | Uganda | `ug_tin` | Uganda Tax Identification Number | + // | Ukraine | `ua_vat` | Ukrainian VAT | + // | United Arab Emirates | `ae_trn` | United Arab Emirates TRN | + // | United Kingdom | `gb_vat` | United Kingdom VAT Number | + // | United States | `us_ein` | United States EIN | + // | Uruguay | `uy_ruc` | Uruguayan RUC Number | + // | Uzbekistan | `uz_tin` | Uzbekistan TIN Number | + // | Uzbekistan | `uz_vat` | Uzbekistan VAT Number | + // | Venezuela | `ve_rif` | Venezuelan RIF Number | + // | Vietnam | `vn_tin` | Vietnamese Tax ID Number | + // | Zambia | `zm_tin` | Zambia Tax Identification Number | + // | Zimbabwe | `zw_tin` | Zimbabwe Tax Identification Number | + CustomerTaxID shared.CustomerTaxID `json:"customer_tax_id,required,nullable"` + // When the invoice payment is due. The due date is null if the invoice is not yet + // finalized. + DueDate time.Time `json:"due_date,required,nullable" format:"date-time"` + // If the invoice has a status of `draft`, this will be the time that the invoice + // will be eligible to be issued, otherwise it will be `null`. If `auto-issue` is + // true, the invoice will automatically begin issuing at this time. + EligibleToIssueAt time.Time `json:"eligible_to_issue_at,required,nullable" format:"date-time"` + // A URL for the customer-facing invoice portal. This URL expires 30 days after the + // invoice's due date, or 60 days after being re-generated through the UI. + HostedInvoiceURL string `json:"hosted_invoice_url,required,nullable"` + // The scheduled date of the invoice + InvoiceDate time.Time `json:"invoice_date,required" format:"date-time"` + // Automatically generated invoice number to help track and reconcile invoices. + // Invoice numbers have a prefix such as `RFOBWG`. These can be sequential per + // account or customer. + InvoiceNumber string `json:"invoice_number,required"` + // The link to download the PDF representation of the `Invoice`. + InvoicePdf string `json:"invoice_pdf,required,nullable"` + InvoiceSource InvoiceListSummaryResponseInvoiceSource `json:"invoice_source,required"` + // If the invoice failed to issue, this will be the last time it failed to issue + // (even if it is now in a different state.) + IssueFailedAt time.Time `json:"issue_failed_at,required,nullable" format:"date-time"` + // If the invoice has been issued, this will be the time it transitioned to + // `issued` (even if it is now in a different state.) + IssuedAt time.Time `json:"issued_at,required,nullable" format:"date-time"` + // Free-form text which is available on the invoice PDF and the Orb invoice portal. + Memo string `json:"memo,required,nullable"` + // User specified key-value pairs for the resource. If not present, this defaults + // to an empty dictionary. Individual keys can be removed by setting the value to + // `null`, and the entire metadata mapping can be cleared by setting `metadata` to + // `null`. + Metadata map[string]string `json:"metadata,required"` + // If the invoice has a status of `paid`, this gives a timestamp when the invoice + // was paid. + PaidAt time.Time `json:"paid_at,required,nullable" format:"date-time"` + // A list of payment attempts associated with the invoice + PaymentAttempts []InvoiceListSummaryResponsePaymentAttempt `json:"payment_attempts,required"` + // If payment was attempted on this invoice but failed, this will be the time of + // the most recent attempt. + PaymentFailedAt time.Time `json:"payment_failed_at,required,nullable" format:"date-time"` + // If payment was attempted on this invoice, this will be the start time of the + // most recent attempt. This field is especially useful for delayed-notification + // payment mechanisms (like bank transfers), where payment can take 3 days or more. + PaymentStartedAt time.Time `json:"payment_started_at,required,nullable" format:"date-time"` + // If the invoice is in draft, this timestamp will reflect when the invoice is + // scheduled to be issued. + ScheduledIssueAt time.Time `json:"scheduled_issue_at,required,nullable" format:"date-time"` + ShippingAddress shared.Address `json:"shipping_address,required,nullable"` + Status InvoiceListSummaryResponseStatus `json:"status,required"` + Subscription shared.SubscriptionMinified `json:"subscription,required,nullable"` + // If the invoice failed to sync, this will be the last time an external invoicing + // provider sync was attempted. This field will always be `null` for invoices using + // Orb Invoicing. + SyncFailedAt time.Time `json:"sync_failed_at,required,nullable" format:"date-time"` + // The total after any minimums and discounts have been applied. + Total string `json:"total,required"` + // If the invoice has a status of `void`, this gives a timestamp when the invoice + // was voided. + VoidedAt time.Time `json:"voided_at,required,nullable" format:"date-time"` + // This is true if the invoice will be automatically issued in the future, and + // false otherwise. + WillAutoIssue bool `json:"will_auto_issue,required"` + JSON invoiceListSummaryResponseJSON `json:"-"` +} + +// invoiceListSummaryResponseJSON contains the JSON metadata for the struct +// [InvoiceListSummaryResponse] +type invoiceListSummaryResponseJSON struct { + ID apijson.Field + AmountDue apijson.Field + AutoCollection apijson.Field + BillingAddress apijson.Field + CreatedAt apijson.Field + CreditNotes apijson.Field + Currency apijson.Field + Customer apijson.Field + CustomerBalanceTransactions apijson.Field + CustomerTaxID apijson.Field + DueDate apijson.Field + EligibleToIssueAt apijson.Field + HostedInvoiceURL apijson.Field + InvoiceDate apijson.Field + InvoiceNumber apijson.Field + InvoicePdf apijson.Field + InvoiceSource apijson.Field + IssueFailedAt apijson.Field + IssuedAt apijson.Field + Memo apijson.Field + Metadata apijson.Field + PaidAt apijson.Field + PaymentAttempts apijson.Field + PaymentFailedAt apijson.Field + PaymentStartedAt apijson.Field + ScheduledIssueAt apijson.Field + ShippingAddress apijson.Field + Status apijson.Field + Subscription apijson.Field + SyncFailedAt apijson.Field + Total apijson.Field + VoidedAt apijson.Field + WillAutoIssue apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *InvoiceListSummaryResponse) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r invoiceListSummaryResponseJSON) RawJSON() string { + return r.raw +} + +type InvoiceListSummaryResponseAutoCollection struct { + // True only if auto-collection is enabled for this invoice. + Enabled bool `json:"enabled,required,nullable"` + // If the invoice is scheduled for auto-collection, this field will reflect when + // the next attempt will occur. If dunning has been exhausted, or auto-collection + // is not enabled for this invoice, this field will be `null`. + NextAttemptAt time.Time `json:"next_attempt_at,required,nullable" format:"date-time"` + // Number of auto-collection payment attempts. + NumAttempts int64 `json:"num_attempts,required,nullable"` + // If Orb has ever attempted payment auto-collection for this invoice, this field + // will reflect when that attempt occurred. In conjunction with `next_attempt_at`, + // this can be used to tell whether the invoice is currently in dunning (that is, + // `previously_attempted_at` is non-null, and `next_attempt_time` is non-null), or + // if dunning has been exhausted (`previously_attempted_at` is non-null, but + // `next_attempt_time` is null). + PreviouslyAttemptedAt time.Time `json:"previously_attempted_at,required,nullable" format:"date-time"` + JSON invoiceListSummaryResponseAutoCollectionJSON `json:"-"` +} + +// invoiceListSummaryResponseAutoCollectionJSON contains the JSON metadata for the +// struct [InvoiceListSummaryResponseAutoCollection] +type invoiceListSummaryResponseAutoCollectionJSON struct { + Enabled apijson.Field + NextAttemptAt apijson.Field + NumAttempts apijson.Field + PreviouslyAttemptedAt apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *InvoiceListSummaryResponseAutoCollection) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r invoiceListSummaryResponseAutoCollectionJSON) RawJSON() string { + return r.raw +} + +type InvoiceListSummaryResponseCreditNote struct { + ID string `json:"id,required"` + CreditNoteNumber string `json:"credit_note_number,required"` + // An optional memo supplied on the credit note. + Memo string `json:"memo,required,nullable"` + Reason string `json:"reason,required"` + Total string `json:"total,required"` + Type string `json:"type,required"` + // If the credit note has a status of `void`, this gives a timestamp when the + // credit note was voided. + VoidedAt time.Time `json:"voided_at,required,nullable" format:"date-time"` + JSON invoiceListSummaryResponseCreditNoteJSON `json:"-"` +} + +// invoiceListSummaryResponseCreditNoteJSON contains the JSON metadata for the +// struct [InvoiceListSummaryResponseCreditNote] +type invoiceListSummaryResponseCreditNoteJSON struct { + ID apijson.Field + CreditNoteNumber apijson.Field + Memo apijson.Field + Reason apijson.Field + Total apijson.Field + Type apijson.Field + VoidedAt apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *InvoiceListSummaryResponseCreditNote) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r invoiceListSummaryResponseCreditNoteJSON) RawJSON() string { + return r.raw +} + +type InvoiceListSummaryResponseCustomerBalanceTransaction struct { + // A unique id for this transaction. + ID string `json:"id,required"` + Action InvoiceListSummaryResponseCustomerBalanceTransactionsAction `json:"action,required"` + // The value of the amount changed in the transaction. + Amount string `json:"amount,required"` + // The creation time of this transaction. + CreatedAt time.Time `json:"created_at,required" format:"date-time"` + CreditNote shared.CreditNoteTiny `json:"credit_note,required,nullable"` + // An optional description provided for manual customer balance adjustments. + Description string `json:"description,required,nullable"` + // The new value of the customer's balance prior to the transaction, in the + // customer's currency. + EndingBalance string `json:"ending_balance,required"` + Invoice shared.InvoiceTiny `json:"invoice,required,nullable"` + // The original value of the customer's balance prior to the transaction, in the + // customer's currency. + StartingBalance string `json:"starting_balance,required"` + Type InvoiceListSummaryResponseCustomerBalanceTransactionsType `json:"type,required"` + JSON invoiceListSummaryResponseCustomerBalanceTransactionJSON `json:"-"` +} + +// invoiceListSummaryResponseCustomerBalanceTransactionJSON contains the JSON +// metadata for the struct [InvoiceListSummaryResponseCustomerBalanceTransaction] +type invoiceListSummaryResponseCustomerBalanceTransactionJSON struct { + ID apijson.Field + Action apijson.Field + Amount apijson.Field + CreatedAt apijson.Field + CreditNote apijson.Field + Description apijson.Field + EndingBalance apijson.Field + Invoice apijson.Field + StartingBalance apijson.Field + Type apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *InvoiceListSummaryResponseCustomerBalanceTransaction) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r invoiceListSummaryResponseCustomerBalanceTransactionJSON) RawJSON() string { + return r.raw +} + +type InvoiceListSummaryResponseCustomerBalanceTransactionsAction string + +const ( + InvoiceListSummaryResponseCustomerBalanceTransactionsActionAppliedToInvoice InvoiceListSummaryResponseCustomerBalanceTransactionsAction = "applied_to_invoice" + InvoiceListSummaryResponseCustomerBalanceTransactionsActionManualAdjustment InvoiceListSummaryResponseCustomerBalanceTransactionsAction = "manual_adjustment" + InvoiceListSummaryResponseCustomerBalanceTransactionsActionProratedRefund InvoiceListSummaryResponseCustomerBalanceTransactionsAction = "prorated_refund" + InvoiceListSummaryResponseCustomerBalanceTransactionsActionRevertProratedRefund InvoiceListSummaryResponseCustomerBalanceTransactionsAction = "revert_prorated_refund" + InvoiceListSummaryResponseCustomerBalanceTransactionsActionReturnFromVoiding InvoiceListSummaryResponseCustomerBalanceTransactionsAction = "return_from_voiding" + InvoiceListSummaryResponseCustomerBalanceTransactionsActionCreditNoteApplied InvoiceListSummaryResponseCustomerBalanceTransactionsAction = "credit_note_applied" + InvoiceListSummaryResponseCustomerBalanceTransactionsActionCreditNoteVoided InvoiceListSummaryResponseCustomerBalanceTransactionsAction = "credit_note_voided" + InvoiceListSummaryResponseCustomerBalanceTransactionsActionOverpaymentRefund InvoiceListSummaryResponseCustomerBalanceTransactionsAction = "overpayment_refund" + InvoiceListSummaryResponseCustomerBalanceTransactionsActionExternalPayment InvoiceListSummaryResponseCustomerBalanceTransactionsAction = "external_payment" + InvoiceListSummaryResponseCustomerBalanceTransactionsActionSmallInvoiceCarryover InvoiceListSummaryResponseCustomerBalanceTransactionsAction = "small_invoice_carryover" +) + +func (r InvoiceListSummaryResponseCustomerBalanceTransactionsAction) IsKnown() bool { + switch r { + case InvoiceListSummaryResponseCustomerBalanceTransactionsActionAppliedToInvoice, InvoiceListSummaryResponseCustomerBalanceTransactionsActionManualAdjustment, InvoiceListSummaryResponseCustomerBalanceTransactionsActionProratedRefund, InvoiceListSummaryResponseCustomerBalanceTransactionsActionRevertProratedRefund, InvoiceListSummaryResponseCustomerBalanceTransactionsActionReturnFromVoiding, InvoiceListSummaryResponseCustomerBalanceTransactionsActionCreditNoteApplied, InvoiceListSummaryResponseCustomerBalanceTransactionsActionCreditNoteVoided, InvoiceListSummaryResponseCustomerBalanceTransactionsActionOverpaymentRefund, InvoiceListSummaryResponseCustomerBalanceTransactionsActionExternalPayment, InvoiceListSummaryResponseCustomerBalanceTransactionsActionSmallInvoiceCarryover: + return true + } + return false +} + +type InvoiceListSummaryResponseCustomerBalanceTransactionsType string + +const ( + InvoiceListSummaryResponseCustomerBalanceTransactionsTypeIncrement InvoiceListSummaryResponseCustomerBalanceTransactionsType = "increment" + InvoiceListSummaryResponseCustomerBalanceTransactionsTypeDecrement InvoiceListSummaryResponseCustomerBalanceTransactionsType = "decrement" +) + +func (r InvoiceListSummaryResponseCustomerBalanceTransactionsType) IsKnown() bool { + switch r { + case InvoiceListSummaryResponseCustomerBalanceTransactionsTypeIncrement, InvoiceListSummaryResponseCustomerBalanceTransactionsTypeDecrement: + return true + } + return false +} + +type InvoiceListSummaryResponseInvoiceSource string + +const ( + InvoiceListSummaryResponseInvoiceSourceSubscription InvoiceListSummaryResponseInvoiceSource = "subscription" + InvoiceListSummaryResponseInvoiceSourcePartial InvoiceListSummaryResponseInvoiceSource = "partial" + InvoiceListSummaryResponseInvoiceSourceOneOff InvoiceListSummaryResponseInvoiceSource = "one_off" +) + +func (r InvoiceListSummaryResponseInvoiceSource) IsKnown() bool { + switch r { + case InvoiceListSummaryResponseInvoiceSourceSubscription, InvoiceListSummaryResponseInvoiceSourcePartial, InvoiceListSummaryResponseInvoiceSourceOneOff: + return true + } + return false +} + +type InvoiceListSummaryResponsePaymentAttempt struct { + // The ID of the payment attempt. + ID string `json:"id,required"` + // The amount of the payment attempt. + Amount string `json:"amount,required"` + // The time at which the payment attempt was created. + CreatedAt time.Time `json:"created_at,required" format:"date-time"` + // The payment provider that attempted to collect the payment. + PaymentProvider InvoiceListSummaryResponsePaymentAttemptsPaymentProvider `json:"payment_provider,required,nullable"` + // The ID of the payment attempt in the payment provider. + PaymentProviderID string `json:"payment_provider_id,required,nullable"` + // URL to the downloadable PDF version of the receipt. This field will be `null` + // for payment attempts that did not succeed. + ReceiptPdf string `json:"receipt_pdf,required,nullable"` + // Whether the payment attempt succeeded. + Succeeded bool `json:"succeeded,required"` + JSON invoiceListSummaryResponsePaymentAttemptJSON `json:"-"` +} + +// invoiceListSummaryResponsePaymentAttemptJSON contains the JSON metadata for the +// struct [InvoiceListSummaryResponsePaymentAttempt] +type invoiceListSummaryResponsePaymentAttemptJSON struct { + ID apijson.Field + Amount apijson.Field + CreatedAt apijson.Field + PaymentProvider apijson.Field + PaymentProviderID apijson.Field + ReceiptPdf apijson.Field + Succeeded apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *InvoiceListSummaryResponsePaymentAttempt) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r invoiceListSummaryResponsePaymentAttemptJSON) RawJSON() string { + return r.raw +} + +// The payment provider that attempted to collect the payment. +type InvoiceListSummaryResponsePaymentAttemptsPaymentProvider string + +const ( + InvoiceListSummaryResponsePaymentAttemptsPaymentProviderStripe InvoiceListSummaryResponsePaymentAttemptsPaymentProvider = "stripe" +) + +func (r InvoiceListSummaryResponsePaymentAttemptsPaymentProvider) IsKnown() bool { + switch r { + case InvoiceListSummaryResponsePaymentAttemptsPaymentProviderStripe: + return true + } + return false +} + +type InvoiceListSummaryResponseStatus string + +const ( + InvoiceListSummaryResponseStatusIssued InvoiceListSummaryResponseStatus = "issued" + InvoiceListSummaryResponseStatusPaid InvoiceListSummaryResponseStatus = "paid" + InvoiceListSummaryResponseStatusSynced InvoiceListSummaryResponseStatus = "synced" + InvoiceListSummaryResponseStatusVoid InvoiceListSummaryResponseStatus = "void" + InvoiceListSummaryResponseStatusDraft InvoiceListSummaryResponseStatus = "draft" +) + +func (r InvoiceListSummaryResponseStatus) IsKnown() bool { + switch r { + case InvoiceListSummaryResponseStatusIssued, InvoiceListSummaryResponseStatusPaid, InvoiceListSummaryResponseStatusSynced, InvoiceListSummaryResponseStatusVoid, InvoiceListSummaryResponseStatusDraft: + return true + } + return false +} + +type InvoiceNewParams struct { + // An ISO 4217 currency string. Must be the same as the customer's currency if it + // is set. + Currency param.Field[string] `json:"currency,required"` + // Optional invoice date to set. Must be in the past, if not set, `invoice_date` is + // set to the current time in the customer's timezone. + InvoiceDate param.Field[time.Time] `json:"invoice_date,required" format:"date-time"` + LineItems param.Field[[]InvoiceNewParamsLineItem] `json:"line_items,required"` + // The id of the `Customer` to create this invoice for. One of `customer_id` and + // `external_customer_id` are required. + CustomerID param.Field[string] `json:"customer_id"` + // An optional discount to attach to the invoice. + Discount param.Field[shared.DiscountUnionParam] `json:"discount"` + // An optional custom due date for the invoice. If not set, the due date will be + // calculated based on the `net_terms` value. + DueDate param.Field[time.Time] `json:"due_date" format:"date-time"` + // The `external_customer_id` of the `Customer` to create this invoice for. One of + // `customer_id` and `external_customer_id` are required. + ExternalCustomerID param.Field[string] `json:"external_customer_id"` + // An optional memo to attach to the invoice. If no memo is provided, we will + // attach the default memo + Memo param.Field[string] `json:"memo"` + // User-specified key/value pairs for the resource. Individual keys can be removed + // by setting the value to `null`, and the entire metadata mapping can be cleared + // by setting `metadata` to `null`. + Metadata param.Field[map[string]string] `json:"metadata"` + // The net terms determines the due date of the invoice. Due date is calculated + // based on the invoice or issuance date, depending on the account's configured due + // date calculation method. A value of '0' here represents that the invoice is due + // on issue, whereas a value of '30' represents that the customer has 30 days to + // pay the invoice. Do not set this field if you want to set a custom due date. + NetTerms param.Field[int64] `json:"net_terms"` + // When true, this invoice will be submitted for issuance upon creation. When + // false, the resulting invoice will require manual review to issue. Defaulted to + // false. + WillAutoIssue param.Field[bool] `json:"will_auto_issue"` +} + +func (r InvoiceNewParams) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} + +type InvoiceNewParamsLineItem struct { + // A date string to specify the line item's end date in the customer's timezone. + EndDate param.Field[time.Time] `json:"end_date,required" format:"date"` + ItemID param.Field[string] `json:"item_id,required"` + ModelType param.Field[InvoiceNewParamsLineItemsModelType] `json:"model_type,required"` + // The name of the line item. + Name param.Field[string] `json:"name,required"` + // The number of units on the line item + Quantity param.Field[float64] `json:"quantity,required"` + // A date string to specify the line item's start date in the customer's timezone. + StartDate param.Field[time.Time] `json:"start_date,required" format:"date"` + // Configuration for unit pricing + UnitConfig param.Field[shared.UnitConfigParam] `json:"unit_config,required"` +} + +func (r InvoiceNewParamsLineItem) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} + +type InvoiceNewParamsLineItemsModelType string + +const ( + InvoiceNewParamsLineItemsModelTypeUnit InvoiceNewParamsLineItemsModelType = "unit" +) + +func (r InvoiceNewParamsLineItemsModelType) IsKnown() bool { + switch r { + case InvoiceNewParamsLineItemsModelTypeUnit: + return true + } + return false +} + +type InvoiceUpdateParams struct { + // An optional custom due date for the invoice. If not set, the due date will be + // calculated based on the `net_terms` value. + DueDate param.Field[time.Time] `json:"due_date" format:"date-time"` + // The date of the invoice. Can only be modified for one-off draft invoices. + InvoiceDate param.Field[time.Time] `json:"invoice_date" format:"date-time"` + // User-specified key/value pairs for the resource. Individual keys can be removed + // by setting the value to `null`, and the entire metadata mapping can be cleared + // by setting `metadata` to `null`. + Metadata param.Field[map[string]string] `json:"metadata"` + // The net terms determines the due date of the invoice. Due date is calculated + // based on the invoice or issuance date, depending on the account's configured due + // date calculation method. A value of '0' here represents that the invoice is due + // on issue, whereas a value of '30' represents that the customer has 30 days to + // pay the invoice. Do not set this field if you want to set a custom due date. + NetTerms param.Field[int64] `json:"net_terms"` +} + +func (r InvoiceUpdateParams) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} + +type InvoiceListParams struct { + Amount param.Field[string] `query:"amount"` + AmountGt param.Field[string] `query:"amount[gt]"` + AmountLt param.Field[string] `query:"amount[lt]"` + // Cursor for pagination. This can be populated by the `next_cursor` value returned + // from the initial request. + Cursor param.Field[string] `query:"cursor"` + CustomerID param.Field[string] `query:"customer_id"` + DateType param.Field[InvoiceListParamsDateType] `query:"date_type"` + DueDate param.Field[time.Time] `query:"due_date" format:"date"` + // Filters invoices by their due dates within a specific time range in the past. + // Specify the range as a number followed by 'd' (days) or 'm' (months). For + // example, '7d' filters invoices due in the last 7 days, and '2m' filters those + // due in the last 2 months. + DueDateWindow param.Field[string] `query:"due_date_window"` + DueDateGt param.Field[time.Time] `query:"due_date[gt]" format:"date"` + DueDateLt param.Field[time.Time] `query:"due_date[lt]" format:"date"` + ExternalCustomerID param.Field[string] `query:"external_customer_id"` + InvoiceDateGt param.Field[time.Time] `query:"invoice_date[gt]" format:"date-time"` + InvoiceDateGte param.Field[time.Time] `query:"invoice_date[gte]" format:"date-time"` + InvoiceDateLt param.Field[time.Time] `query:"invoice_date[lt]" format:"date-time"` + InvoiceDateLte param.Field[time.Time] `query:"invoice_date[lte]" format:"date-time"` + IsRecurring param.Field[bool] `query:"is_recurring"` + // The number of items to fetch. Defaults to 20. + Limit param.Field[int64] `query:"limit"` + Status param.Field[[]InvoiceListParamsStatus] `query:"status"` + SubscriptionID param.Field[string] `query:"subscription_id"` +} + +// URLQuery serializes [InvoiceListParams]'s query parameters as `url.Values`. +func (r InvoiceListParams) URLQuery() (v url.Values) { + return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{ + ArrayFormat: apiquery.ArrayQueryFormatBrackets, + NestedFormat: apiquery.NestedQueryFormatBrackets, + }) +} + +type InvoiceListParamsDateType string + +const ( + InvoiceListParamsDateTypeDueDate InvoiceListParamsDateType = "due_date" + InvoiceListParamsDateTypeInvoiceDate InvoiceListParamsDateType = "invoice_date" +) + +func (r InvoiceListParamsDateType) IsKnown() bool { + switch r { + case InvoiceListParamsDateTypeDueDate, InvoiceListParamsDateTypeInvoiceDate: + return true + } + return false +} + +type InvoiceListParamsStatus string + +const ( + InvoiceListParamsStatusDraft InvoiceListParamsStatus = "draft" + InvoiceListParamsStatusIssued InvoiceListParamsStatus = "issued" + InvoiceListParamsStatusPaid InvoiceListParamsStatus = "paid" + InvoiceListParamsStatusSynced InvoiceListParamsStatus = "synced" + InvoiceListParamsStatusVoid InvoiceListParamsStatus = "void" +) + +func (r InvoiceListParamsStatus) IsKnown() bool { + switch r { + case InvoiceListParamsStatusDraft, InvoiceListParamsStatusIssued, InvoiceListParamsStatusPaid, InvoiceListParamsStatusSynced, InvoiceListParamsStatusVoid: + return true + } + return false +} + +type InvoiceFetchUpcomingParams struct { + SubscriptionID param.Field[string] `query:"subscription_id,required"` +} + +// URLQuery serializes [InvoiceFetchUpcomingParams]'s query parameters as +// `url.Values`. +func (r InvoiceFetchUpcomingParams) URLQuery() (v url.Values) { + return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{ + ArrayFormat: apiquery.ArrayQueryFormatBrackets, + NestedFormat: apiquery.NestedQueryFormatBrackets, + }) +} + +type InvoiceIssueParams struct { + // If true, the invoice will be issued synchronously. If false, the invoice will be + // issued asynchronously. The synchronous option is only available for invoices + // that have no usage fees. If the invoice is configured to sync to an external + // provider, a successful response from this endpoint guarantees the invoice is + // present in the provider. + Synchronous param.Field[bool] `json:"synchronous"` +} + +func (r InvoiceIssueParams) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} + +type InvoiceListSummaryParams struct { + Amount param.Field[string] `query:"amount"` + AmountGt param.Field[string] `query:"amount[gt]"` + AmountLt param.Field[string] `query:"amount[lt]"` + // Cursor for pagination. This can be populated by the `next_cursor` value returned + // from the initial request. + Cursor param.Field[string] `query:"cursor"` + CustomerID param.Field[string] `query:"customer_id"` + DateType param.Field[InvoiceListSummaryParamsDateType] `query:"date_type"` + DueDate param.Field[time.Time] `query:"due_date" format:"date"` + // Filters invoices by their due dates within a specific time range in the past. + // Specify the range as a number followed by 'd' (days) or 'm' (months). For + // example, '7d' filters invoices due in the last 7 days, and '2m' filters those + // due in the last 2 months. + DueDateWindow param.Field[string] `query:"due_date_window"` + DueDateGt param.Field[time.Time] `query:"due_date[gt]" format:"date"` + DueDateLt param.Field[time.Time] `query:"due_date[lt]" format:"date"` + ExternalCustomerID param.Field[string] `query:"external_customer_id"` + InvoiceDateGt param.Field[time.Time] `query:"invoice_date[gt]" format:"date-time"` + InvoiceDateGte param.Field[time.Time] `query:"invoice_date[gte]" format:"date-time"` + InvoiceDateLt param.Field[time.Time] `query:"invoice_date[lt]" format:"date-time"` + InvoiceDateLte param.Field[time.Time] `query:"invoice_date[lte]" format:"date-time"` + IsRecurring param.Field[bool] `query:"is_recurring"` + // The number of items to fetch. Defaults to 20. + Limit param.Field[int64] `query:"limit"` + Status param.Field[InvoiceListSummaryParamsStatus] `query:"status"` + SubscriptionID param.Field[string] `query:"subscription_id"` +} + +// URLQuery serializes [InvoiceListSummaryParams]'s query parameters as +// `url.Values`. +func (r InvoiceListSummaryParams) URLQuery() (v url.Values) { + return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{ + ArrayFormat: apiquery.ArrayQueryFormatBrackets, + NestedFormat: apiquery.NestedQueryFormatBrackets, + }) +} + +type InvoiceListSummaryParamsDateType string + +const ( + InvoiceListSummaryParamsDateTypeDueDate InvoiceListSummaryParamsDateType = "due_date" + InvoiceListSummaryParamsDateTypeInvoiceDate InvoiceListSummaryParamsDateType = "invoice_date" +) + +func (r InvoiceListSummaryParamsDateType) IsKnown() bool { + switch r { + case InvoiceListSummaryParamsDateTypeDueDate, InvoiceListSummaryParamsDateTypeInvoiceDate: + return true + } + return false +} + +type InvoiceListSummaryParamsStatus string + +const ( + InvoiceListSummaryParamsStatusDraft InvoiceListSummaryParamsStatus = "draft" + InvoiceListSummaryParamsStatusIssued InvoiceListSummaryParamsStatus = "issued" + InvoiceListSummaryParamsStatusPaid InvoiceListSummaryParamsStatus = "paid" + InvoiceListSummaryParamsStatusSynced InvoiceListSummaryParamsStatus = "synced" + InvoiceListSummaryParamsStatusVoid InvoiceListSummaryParamsStatus = "void" +) + +func (r InvoiceListSummaryParamsStatus) IsKnown() bool { + switch r { + case InvoiceListSummaryParamsStatusDraft, InvoiceListSummaryParamsStatusIssued, InvoiceListSummaryParamsStatusPaid, InvoiceListSummaryParamsStatusSynced, InvoiceListSummaryParamsStatusVoid: + return true + } + return false } type InvoiceMarkPaidParams struct { diff --git a/invoice_test.go b/invoice_test.go index 998b087..d9bea65 100644 --- a/invoice_test.go +++ b/invoice_test.go @@ -147,6 +147,32 @@ func TestInvoiceListWithOptionalParams(t *testing.T) { } } +func TestInvoiceDeleteLineItem(t *testing.T) { + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := orb.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + err := client.Invoices.DeleteLineItem( + context.TODO(), + "invoice_id", + "line_item_id", + ) + if err != nil { + var apierr *orb.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} + func TestInvoiceFetch(t *testing.T) { baseURL := "http://localhost:4010" if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { @@ -221,6 +247,48 @@ func TestInvoiceIssueWithOptionalParams(t *testing.T) { } } +func TestInvoiceListSummaryWithOptionalParams(t *testing.T) { + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := orb.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + _, err := client.Invoices.ListSummary(context.TODO(), orb.InvoiceListSummaryParams{ + Amount: orb.F("amount"), + AmountGt: orb.F("amount[gt]"), + AmountLt: orb.F("amount[lt]"), + Cursor: orb.F("cursor"), + CustomerID: orb.F("customer_id"), + DateType: orb.F(orb.InvoiceListSummaryParamsDateTypeDueDate), + DueDate: orb.F(time.Now()), + DueDateWindow: orb.F("due_date_window"), + DueDateGt: orb.F(time.Now()), + DueDateLt: orb.F(time.Now()), + ExternalCustomerID: orb.F("external_customer_id"), + InvoiceDateGt: orb.F(time.Now()), + InvoiceDateGte: orb.F(time.Now()), + InvoiceDateLt: orb.F(time.Now()), + InvoiceDateLte: orb.F(time.Now()), + IsRecurring: orb.F(true), + Limit: orb.F(int64(1)), + Status: orb.F(orb.InvoiceListSummaryParamsStatusDraft), + SubscriptionID: orb.F("subscription_id"), + }) + if err != nil { + var apierr *orb.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} + func TestInvoiceMarkPaidWithOptionalParams(t *testing.T) { baseURL := "http://localhost:4010" if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { diff --git a/plan.go b/plan.go index e568f06..c391de0 100644 --- a/plan.go +++ b/plan.go @@ -31,6 +31,7 @@ import ( type PlanService struct { Options []option.RequestOption ExternalPlanID *PlanExternalPlanIDService + Migrations *PlanMigrationService } // NewPlanService generates a new service that applies the given options to each @@ -40,6 +41,7 @@ func NewPlanService(opts ...option.RequestOption) (r *PlanService) { r = &PlanService{} r.Options = opts r.ExternalPlanID = NewPlanExternalPlanIDService(opts...) + r.Migrations = NewPlanMigrationService(opts...) return } diff --git a/planmigration.go b/planmigration.go new file mode 100644 index 0000000..44c5328 --- /dev/null +++ b/planmigration.go @@ -0,0 +1,384 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package orb + +import ( + "context" + "errors" + "fmt" + "net/http" + "net/url" + "reflect" + "slices" + + "github.com/orbcorp/orb-go/internal/apijson" + "github.com/orbcorp/orb-go/internal/apiquery" + "github.com/orbcorp/orb-go/internal/param" + "github.com/orbcorp/orb-go/internal/requestconfig" + "github.com/orbcorp/orb-go/option" + "github.com/orbcorp/orb-go/packages/pagination" + "github.com/orbcorp/orb-go/shared" + "github.com/tidwall/gjson" +) + +// PlanMigrationService contains methods and other services that help with +// interacting with the orb API. +// +// Note, unlike clients, this service does not read variables from the environment +// automatically. You should not instantiate this service directly, and instead use +// the [NewPlanMigrationService] method instead. +type PlanMigrationService struct { + Options []option.RequestOption +} + +// NewPlanMigrationService generates a new service that applies the given options +// to each request. These options are applied after the parent client's options (if +// there is one), and before any request-specific options. +func NewPlanMigrationService(opts ...option.RequestOption) (r *PlanMigrationService) { + r = &PlanMigrationService{} + r.Options = opts + return +} + +// Fetch migration +func (r *PlanMigrationService) Get(ctx context.Context, planID string, migrationID string, opts ...option.RequestOption) (res *PlanMigrationGetResponse, err error) { + opts = slices.Concat(r.Options, opts) + if planID == "" { + err = errors.New("missing required plan_id parameter") + return + } + if migrationID == "" { + err = errors.New("missing required migration_id parameter") + return + } + path := fmt.Sprintf("plans/%s/migrations/%s", planID, migrationID) + err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, nil, &res, opts...) + return +} + +// This endpoint returns a list of all migrations for a plan. The list of +// migrations is ordered starting from the most recently created migration. The +// response also includes pagination_metadata, which lets the caller retrieve the +// next page of results if they exist. +func (r *PlanMigrationService) List(ctx context.Context, planID string, query PlanMigrationListParams, opts ...option.RequestOption) (res *pagination.Page[PlanMigrationListResponse], err error) { + var raw *http.Response + opts = slices.Concat(r.Options, opts) + opts = append([]option.RequestOption{option.WithResponseInto(&raw)}, opts...) + if planID == "" { + err = errors.New("missing required plan_id parameter") + return + } + path := fmt.Sprintf("plans/%s/migrations", planID) + cfg, err := requestconfig.NewRequestConfig(ctx, http.MethodGet, path, query, &res, opts...) + if err != nil { + return nil, err + } + err = cfg.Execute() + if err != nil { + return nil, err + } + res.SetPageConfig(cfg, raw) + return res, nil +} + +// This endpoint returns a list of all migrations for a plan. The list of +// migrations is ordered starting from the most recently created migration. The +// response also includes pagination_metadata, which lets the caller retrieve the +// next page of results if they exist. +func (r *PlanMigrationService) ListAutoPaging(ctx context.Context, planID string, query PlanMigrationListParams, opts ...option.RequestOption) *pagination.PageAutoPager[PlanMigrationListResponse] { + return pagination.NewPageAutoPager(r.List(ctx, planID, query, opts...)) +} + +// This endpoint cancels a migration. +func (r *PlanMigrationService) Cancel(ctx context.Context, planID string, migrationID string, opts ...option.RequestOption) (res *PlanMigrationCancelResponse, err error) { + opts = slices.Concat(r.Options, opts) + if planID == "" { + err = errors.New("missing required plan_id parameter") + return + } + if migrationID == "" { + err = errors.New("missing required migration_id parameter") + return + } + path := fmt.Sprintf("plans/%s/migrations/%s/cancel", planID, migrationID) + err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, nil, &res, opts...) + return +} + +type PlanMigrationGetResponse struct { + ID string `json:"id,required"` + EffectiveTime PlanMigrationGetResponseEffectiveTimeUnion `json:"effective_time,required,nullable" format:"date"` + PlanID string `json:"plan_id,required"` + Status PlanMigrationGetResponseStatus `json:"status,required"` + JSON planMigrationGetResponseJSON `json:"-"` +} + +// planMigrationGetResponseJSON contains the JSON metadata for the struct +// [PlanMigrationGetResponse] +type planMigrationGetResponseJSON struct { + ID apijson.Field + EffectiveTime apijson.Field + PlanID apijson.Field + Status apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *PlanMigrationGetResponse) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r planMigrationGetResponseJSON) RawJSON() string { + return r.raw +} + +// Union satisfied by [shared.UnionTime], [shared.UnionTime] or +// [PlanMigrationGetResponseEffectiveTimeString]. +type PlanMigrationGetResponseEffectiveTimeUnion interface { + ImplementsPlanMigrationGetResponseEffectiveTimeUnion() +} + +func init() { + apijson.RegisterUnion( + reflect.TypeOf((*PlanMigrationGetResponseEffectiveTimeUnion)(nil)).Elem(), + "", + apijson.UnionVariant{ + TypeFilter: gjson.String, + Type: reflect.TypeOf(shared.UnionTime(shared.UnionTime{})), + }, + apijson.UnionVariant{ + TypeFilter: gjson.String, + Type: reflect.TypeOf(shared.UnionTime(shared.UnionTime{})), + }, + apijson.UnionVariant{ + TypeFilter: gjson.String, + Type: reflect.TypeOf(PlanMigrationGetResponseEffectiveTimeString("")), + }, + ) +} + +type PlanMigrationGetResponseEffectiveTimeString string + +const ( + PlanMigrationGetResponseEffectiveTimeStringEndOfTerm PlanMigrationGetResponseEffectiveTimeString = "end_of_term" +) + +func (r PlanMigrationGetResponseEffectiveTimeString) IsKnown() bool { + switch r { + case PlanMigrationGetResponseEffectiveTimeStringEndOfTerm: + return true + } + return false +} + +func (r PlanMigrationGetResponseEffectiveTimeString) ImplementsPlanMigrationGetResponseEffectiveTimeUnion() { +} + +type PlanMigrationGetResponseStatus string + +const ( + PlanMigrationGetResponseStatusNotStarted PlanMigrationGetResponseStatus = "not_started" + PlanMigrationGetResponseStatusInProgress PlanMigrationGetResponseStatus = "in_progress" + PlanMigrationGetResponseStatusCompleted PlanMigrationGetResponseStatus = "completed" + PlanMigrationGetResponseStatusActionNeeded PlanMigrationGetResponseStatus = "action_needed" + PlanMigrationGetResponseStatusCanceled PlanMigrationGetResponseStatus = "canceled" +) + +func (r PlanMigrationGetResponseStatus) IsKnown() bool { + switch r { + case PlanMigrationGetResponseStatusNotStarted, PlanMigrationGetResponseStatusInProgress, PlanMigrationGetResponseStatusCompleted, PlanMigrationGetResponseStatusActionNeeded, PlanMigrationGetResponseStatusCanceled: + return true + } + return false +} + +type PlanMigrationListResponse struct { + ID string `json:"id,required"` + EffectiveTime PlanMigrationListResponseEffectiveTimeUnion `json:"effective_time,required,nullable" format:"date"` + PlanID string `json:"plan_id,required"` + Status PlanMigrationListResponseStatus `json:"status,required"` + JSON planMigrationListResponseJSON `json:"-"` +} + +// planMigrationListResponseJSON contains the JSON metadata for the struct +// [PlanMigrationListResponse] +type planMigrationListResponseJSON struct { + ID apijson.Field + EffectiveTime apijson.Field + PlanID apijson.Field + Status apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *PlanMigrationListResponse) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r planMigrationListResponseJSON) RawJSON() string { + return r.raw +} + +// Union satisfied by [shared.UnionTime], [shared.UnionTime] or +// [PlanMigrationListResponseEffectiveTimeString]. +type PlanMigrationListResponseEffectiveTimeUnion interface { + ImplementsPlanMigrationListResponseEffectiveTimeUnion() +} + +func init() { + apijson.RegisterUnion( + reflect.TypeOf((*PlanMigrationListResponseEffectiveTimeUnion)(nil)).Elem(), + "", + apijson.UnionVariant{ + TypeFilter: gjson.String, + Type: reflect.TypeOf(shared.UnionTime(shared.UnionTime{})), + }, + apijson.UnionVariant{ + TypeFilter: gjson.String, + Type: reflect.TypeOf(shared.UnionTime(shared.UnionTime{})), + }, + apijson.UnionVariant{ + TypeFilter: gjson.String, + Type: reflect.TypeOf(PlanMigrationListResponseEffectiveTimeString("")), + }, + ) +} + +type PlanMigrationListResponseEffectiveTimeString string + +const ( + PlanMigrationListResponseEffectiveTimeStringEndOfTerm PlanMigrationListResponseEffectiveTimeString = "end_of_term" +) + +func (r PlanMigrationListResponseEffectiveTimeString) IsKnown() bool { + switch r { + case PlanMigrationListResponseEffectiveTimeStringEndOfTerm: + return true + } + return false +} + +func (r PlanMigrationListResponseEffectiveTimeString) ImplementsPlanMigrationListResponseEffectiveTimeUnion() { +} + +type PlanMigrationListResponseStatus string + +const ( + PlanMigrationListResponseStatusNotStarted PlanMigrationListResponseStatus = "not_started" + PlanMigrationListResponseStatusInProgress PlanMigrationListResponseStatus = "in_progress" + PlanMigrationListResponseStatusCompleted PlanMigrationListResponseStatus = "completed" + PlanMigrationListResponseStatusActionNeeded PlanMigrationListResponseStatus = "action_needed" + PlanMigrationListResponseStatusCanceled PlanMigrationListResponseStatus = "canceled" +) + +func (r PlanMigrationListResponseStatus) IsKnown() bool { + switch r { + case PlanMigrationListResponseStatusNotStarted, PlanMigrationListResponseStatusInProgress, PlanMigrationListResponseStatusCompleted, PlanMigrationListResponseStatusActionNeeded, PlanMigrationListResponseStatusCanceled: + return true + } + return false +} + +type PlanMigrationCancelResponse struct { + ID string `json:"id,required"` + EffectiveTime PlanMigrationCancelResponseEffectiveTimeUnion `json:"effective_time,required,nullable" format:"date"` + PlanID string `json:"plan_id,required"` + Status PlanMigrationCancelResponseStatus `json:"status,required"` + JSON planMigrationCancelResponseJSON `json:"-"` +} + +// planMigrationCancelResponseJSON contains the JSON metadata for the struct +// [PlanMigrationCancelResponse] +type planMigrationCancelResponseJSON struct { + ID apijson.Field + EffectiveTime apijson.Field + PlanID apijson.Field + Status apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *PlanMigrationCancelResponse) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r planMigrationCancelResponseJSON) RawJSON() string { + return r.raw +} + +// Union satisfied by [shared.UnionTime], [shared.UnionTime] or +// [PlanMigrationCancelResponseEffectiveTimeString]. +type PlanMigrationCancelResponseEffectiveTimeUnion interface { + ImplementsPlanMigrationCancelResponseEffectiveTimeUnion() +} + +func init() { + apijson.RegisterUnion( + reflect.TypeOf((*PlanMigrationCancelResponseEffectiveTimeUnion)(nil)).Elem(), + "", + apijson.UnionVariant{ + TypeFilter: gjson.String, + Type: reflect.TypeOf(shared.UnionTime(shared.UnionTime{})), + }, + apijson.UnionVariant{ + TypeFilter: gjson.String, + Type: reflect.TypeOf(shared.UnionTime(shared.UnionTime{})), + }, + apijson.UnionVariant{ + TypeFilter: gjson.String, + Type: reflect.TypeOf(PlanMigrationCancelResponseEffectiveTimeString("")), + }, + ) +} + +type PlanMigrationCancelResponseEffectiveTimeString string + +const ( + PlanMigrationCancelResponseEffectiveTimeStringEndOfTerm PlanMigrationCancelResponseEffectiveTimeString = "end_of_term" +) + +func (r PlanMigrationCancelResponseEffectiveTimeString) IsKnown() bool { + switch r { + case PlanMigrationCancelResponseEffectiveTimeStringEndOfTerm: + return true + } + return false +} + +func (r PlanMigrationCancelResponseEffectiveTimeString) ImplementsPlanMigrationCancelResponseEffectiveTimeUnion() { +} + +type PlanMigrationCancelResponseStatus string + +const ( + PlanMigrationCancelResponseStatusNotStarted PlanMigrationCancelResponseStatus = "not_started" + PlanMigrationCancelResponseStatusInProgress PlanMigrationCancelResponseStatus = "in_progress" + PlanMigrationCancelResponseStatusCompleted PlanMigrationCancelResponseStatus = "completed" + PlanMigrationCancelResponseStatusActionNeeded PlanMigrationCancelResponseStatus = "action_needed" + PlanMigrationCancelResponseStatusCanceled PlanMigrationCancelResponseStatus = "canceled" +) + +func (r PlanMigrationCancelResponseStatus) IsKnown() bool { + switch r { + case PlanMigrationCancelResponseStatusNotStarted, PlanMigrationCancelResponseStatusInProgress, PlanMigrationCancelResponseStatusCompleted, PlanMigrationCancelResponseStatusActionNeeded, PlanMigrationCancelResponseStatusCanceled: + return true + } + return false +} + +type PlanMigrationListParams struct { + // Cursor for pagination. This can be populated by the `next_cursor` value returned + // from the initial request. + Cursor param.Field[string] `query:"cursor"` + // The number of items to fetch. Defaults to 20. + Limit param.Field[int64] `query:"limit"` +} + +// URLQuery serializes [PlanMigrationListParams]'s query parameters as +// `url.Values`. +func (r PlanMigrationListParams) URLQuery() (v url.Values) { + return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{ + ArrayFormat: apiquery.ArrayQueryFormatBrackets, + NestedFormat: apiquery.NestedQueryFormatBrackets, + }) +} diff --git a/planmigration_test.go b/planmigration_test.go new file mode 100644 index 0000000..5aaeca8 --- /dev/null +++ b/planmigration_test.go @@ -0,0 +1,95 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package orb_test + +import ( + "context" + "errors" + "os" + "testing" + + "github.com/orbcorp/orb-go" + "github.com/orbcorp/orb-go/internal/testutil" + "github.com/orbcorp/orb-go/option" +) + +func TestPlanMigrationGet(t *testing.T) { + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := orb.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + _, err := client.Plans.Migrations.Get( + context.TODO(), + "plan_id", + "migration_id", + ) + if err != nil { + var apierr *orb.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} + +func TestPlanMigrationListWithOptionalParams(t *testing.T) { + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := orb.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + _, err := client.Plans.Migrations.List( + context.TODO(), + "plan_id", + orb.PlanMigrationListParams{ + Cursor: orb.F("cursor"), + Limit: orb.F(int64(1)), + }, + ) + if err != nil { + var apierr *orb.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} + +func TestPlanMigrationCancel(t *testing.T) { + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := orb.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + _, err := client.Plans.Migrations.Cancel( + context.TODO(), + "plan_id", + "migration_id", + ) + if err != nil { + var apierr *orb.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} diff --git a/shared/union.go b/shared/union.go index 98f6400..20c30f2 100644 --- a/shared/union.go +++ b/shared/union.go @@ -8,6 +8,9 @@ import ( type UnionTime time.Time +func (UnionTime) ImplementsPlanMigrationGetResponseEffectiveTimeUnion() {} +func (UnionTime) ImplementsPlanMigrationListResponseEffectiveTimeUnion() {} +func (UnionTime) ImplementsPlanMigrationCancelResponseEffectiveTimeUnion() {} func (UnionTime) ImplementsSubscriptionPriceIntervalsParamsAddStartDateUnion() {} func (UnionTime) ImplementsSubscriptionPriceIntervalsParamsAddEndDateUnion() {} func (UnionTime) ImplementsSubscriptionPriceIntervalsParamsAddAdjustmentsStartDateUnion() {} diff --git a/subscriptionchange.go b/subscriptionchange.go index f82fd7d..ef97ce0 100644 --- a/subscriptionchange.go +++ b/subscriptionchange.go @@ -7,14 +7,17 @@ import ( "errors" "fmt" "net/http" + "net/url" "reflect" "slices" "time" "github.com/orbcorp/orb-go/internal/apijson" + "github.com/orbcorp/orb-go/internal/apiquery" "github.com/orbcorp/orb-go/internal/param" "github.com/orbcorp/orb-go/internal/requestconfig" "github.com/orbcorp/orb-go/option" + "github.com/orbcorp/orb-go/packages/pagination" "github.com/orbcorp/orb-go/shared" "github.com/tidwall/gjson" ) @@ -58,6 +61,33 @@ func (r *SubscriptionChangeService) Get(ctx context.Context, subscriptionChangeI return } +// This endpoint returns a list of pending subscription changes for a customer. Use +// the [Fetch Subscription Change](fetch-subscription-change) endpoint to retrieve +// the expected subscription state after the pending change is applied. +func (r *SubscriptionChangeService) List(ctx context.Context, query SubscriptionChangeListParams, opts ...option.RequestOption) (res *pagination.Page[SubscriptionChangeListResponse], err error) { + var raw *http.Response + opts = slices.Concat(r.Options, opts) + opts = append([]option.RequestOption{option.WithResponseInto(&raw)}, opts...) + path := "subscription_changes" + cfg, err := requestconfig.NewRequestConfig(ctx, http.MethodGet, path, query, &res, opts...) + if err != nil { + return nil, err + } + err = cfg.Execute() + if err != nil { + return nil, err + } + res.SetPageConfig(cfg, raw) + return res, nil +} + +// This endpoint returns a list of pending subscription changes for a customer. Use +// the [Fetch Subscription Change](fetch-subscription-change) endpoint to retrieve +// the expected subscription state after the pending change is applied. +func (r *SubscriptionChangeService) ListAutoPaging(ctx context.Context, query SubscriptionChangeListParams, opts ...option.RequestOption) *pagination.PageAutoPager[SubscriptionChangeListResponse] { + return pagination.NewPageAutoPager(r.List(ctx, query, opts...)) +} + // Apply a subscription change to perform the intended action. If a positive amount // is passed with a request to this endpoint, any eligible invoices that were // created will be issued immediately if they only contain in-advance fees. @@ -421,6 +451,56 @@ func (r SubscriptionChangeGetResponseStatus) IsKnown() bool { return false } +type SubscriptionChangeListResponse struct { + ID string `json:"id,required"` + // Subscription change will be cancelled at this time and can no longer be applied. + ExpirationTime time.Time `json:"expiration_time,required" format:"date-time"` + Status SubscriptionChangeListResponseStatus `json:"status,required"` + SubscriptionID string `json:"subscription_id,required,nullable"` + // When this change was applied. + AppliedAt time.Time `json:"applied_at,nullable" format:"date-time"` + // When this change was cancelled. + CancelledAt time.Time `json:"cancelled_at,nullable" format:"date-time"` + JSON subscriptionChangeListResponseJSON `json:"-"` +} + +// subscriptionChangeListResponseJSON contains the JSON metadata for the struct +// [SubscriptionChangeListResponse] +type subscriptionChangeListResponseJSON struct { + ID apijson.Field + ExpirationTime apijson.Field + Status apijson.Field + SubscriptionID apijson.Field + AppliedAt apijson.Field + CancelledAt apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *SubscriptionChangeListResponse) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r subscriptionChangeListResponseJSON) RawJSON() string { + return r.raw +} + +type SubscriptionChangeListResponseStatus string + +const ( + SubscriptionChangeListResponseStatusPending SubscriptionChangeListResponseStatus = "pending" + SubscriptionChangeListResponseStatusApplied SubscriptionChangeListResponseStatus = "applied" + SubscriptionChangeListResponseStatusCancelled SubscriptionChangeListResponseStatus = "cancelled" +) + +func (r SubscriptionChangeListResponseStatus) IsKnown() bool { + switch r { + case SubscriptionChangeListResponseStatusPending, SubscriptionChangeListResponseStatusApplied, SubscriptionChangeListResponseStatusCancelled: + return true + } + return false +} + // A subscription change represents a desired new subscription / pending change to // an existing subscription. It is a way to first preview the effects on the // subscription as well as any changes/creation of invoices (see @@ -561,6 +641,42 @@ func (r SubscriptionChangeCancelResponseStatus) IsKnown() bool { return false } +type SubscriptionChangeListParams struct { + // Cursor for pagination. This can be populated by the `next_cursor` value returned + // from the initial request. + Cursor param.Field[string] `query:"cursor"` + CustomerID param.Field[string] `query:"customer_id"` + ExternalCustomerID param.Field[string] `query:"external_customer_id"` + // The number of items to fetch. Defaults to 20. + Limit param.Field[int64] `query:"limit"` + Status param.Field[SubscriptionChangeListParamsStatus] `query:"status"` +} + +// URLQuery serializes [SubscriptionChangeListParams]'s query parameters as +// `url.Values`. +func (r SubscriptionChangeListParams) URLQuery() (v url.Values) { + return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{ + ArrayFormat: apiquery.ArrayQueryFormatBrackets, + NestedFormat: apiquery.NestedQueryFormatBrackets, + }) +} + +type SubscriptionChangeListParamsStatus string + +const ( + SubscriptionChangeListParamsStatusPending SubscriptionChangeListParamsStatus = "pending" + SubscriptionChangeListParamsStatusApplied SubscriptionChangeListParamsStatus = "applied" + SubscriptionChangeListParamsStatusCancelled SubscriptionChangeListParamsStatus = "cancelled" +) + +func (r SubscriptionChangeListParamsStatus) IsKnown() bool { + switch r { + case SubscriptionChangeListParamsStatusPending, SubscriptionChangeListParamsStatusApplied, SubscriptionChangeListParamsStatusCancelled: + return true + } + return false +} + type SubscriptionChangeApplyParams struct { // Description to apply to the balance transaction representing this credit. Description param.Field[string] `json:"description"` diff --git a/subscriptionchange_test.go b/subscriptionchange_test.go index e63931c..355fb96 100644 --- a/subscriptionchange_test.go +++ b/subscriptionchange_test.go @@ -36,6 +36,34 @@ func TestSubscriptionChangeGet(t *testing.T) { } } +func TestSubscriptionChangeListWithOptionalParams(t *testing.T) { + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := orb.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + _, err := client.SubscriptionChanges.List(context.TODO(), orb.SubscriptionChangeListParams{ + Cursor: orb.F("cursor"), + CustomerID: orb.F("customer_id"), + ExternalCustomerID: orb.F("external_customer_id"), + Limit: orb.F(int64(1)), + Status: orb.F(orb.SubscriptionChangeListParamsStatusPending), + }) + if err != nil { + var apierr *orb.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} + func TestSubscriptionChangeApplyWithOptionalParams(t *testing.T) { baseURL := "http://localhost:4010" if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok {