From 5a36b17cf02f8e17fb6e5b0b97a08137626ef0ed Mon Sep 17 00:00:00 2001 From: Michael Woodward Date: Tue, 8 Oct 2024 14:39:28 +0100 Subject: [PATCH 1/9] feat: API alignment - Adds Simulation API - Notification Setting filter by traffic_source - New errors --- notification_settings.go | 16 ++ pkg/paddlenotification/shared.go | 4 +- sdk.go | 8 + shared.go | 112 +++++++++++++- simulation_run_events.go | 81 ++++++++++ simulation_runs.go | 113 ++++++++++++++ simulation_types.go | 46 ++++++ simulations.go | 247 +++++++++++++++++++++++++++++++ subscriptions.go | 7 + transactions.go | 7 + 10 files changed, 638 insertions(+), 3 deletions(-) create mode 100644 simulation_run_events.go create mode 100644 simulation_runs.go create mode 100644 simulation_types.go create mode 100644 simulations.go diff --git a/notification_settings.go b/notification_settings.go index 31a5336..0ba6cee 100644 --- a/notification_settings.go +++ b/notification_settings.go @@ -4,6 +4,15 @@ package paddle import "context" +// TrafficSource: Return entities that match the specified traffic source.. +type TrafficSource string + +const ( + TrafficSourcePlatform TrafficSource = "platform" + TrafficSourceSimulation TrafficSource = "simulation" + TrafficSourceAll TrafficSource = "all" +) + // NotificationSettingType: Where notifications should be sent for this destination.. type NotificationSettingType string @@ -61,6 +70,9 @@ type ListNotificationSettingsRequest struct { // Active is a query parameter. // Determine whether returned entities are active (`true`) or not (`false`). Active *bool `in:"query=active;omitempty" json:"-"` + // TrafficSource is a query parameter. + // Return entities that match the specified traffic source. + TrafficSource *string `in:"query=traffic_source;omitempty" json:"-"` } // ListNotificationSettings performs the GET operation on a Notification settings resource. @@ -86,6 +98,8 @@ type CreateNotificationSettingRequest struct { APIVersion *int `json:"api_version,omitempty"` // IncludeSensitiveFields: Whether potentially sensitive fields should be sent to this notification destination. If omitted, defaults to `false`. IncludeSensitiveFields *bool `json:"include_sensitive_fields,omitempty"` + // TrafficSource: Whether Paddle should deliver real platform events, simulation events or both to this notification destination. + TrafficSource *TrafficSource `json:"traffic_source,omitempty"` } // CreateNotificationSetting performs the POST operation on a Notification settings resource. @@ -129,6 +143,8 @@ type UpdateNotificationSettingRequest struct { IncludeSensitiveFields *PatchField[bool] `json:"include_sensitive_fields,omitempty"` // SubscribedEvents: Type of event sent by Paddle, in the format `entity.event_type`. SubscribedEvents *PatchField[[]EventTypeName] `json:"subscribed_events,omitempty"` + // TrafficSource: Whether Paddle should deliver real platform events, simulation events or both to this notification destination. + TrafficSource *PatchField[TrafficSource] `json:"traffic_source,omitempty"` } // UpdateNotificationSetting performs the PATCH operation on a Notification settings resource. diff --git a/pkg/paddlenotification/shared.go b/pkg/paddlenotification/shared.go index 954bc31..731d304 100644 --- a/pkg/paddlenotification/shared.go +++ b/pkg/paddlenotification/shared.go @@ -285,6 +285,9 @@ const ( CountryCodeZM CountryCode = "ZM" ) +// CustomData: Your own structured key-value data. +type CustomData map[string]any + // Status: Whether this entity can be used in Paddle.. type Status string @@ -554,4 +557,3 @@ type Product struct { // UpdatedAt: RFC 3339 datetime string of when this entity was updated. Set automatically by Paddle. UpdatedAt string `json:"updated_at,omitempty"` } -type CustomData map[string]any diff --git a/sdk.go b/sdk.go index 8585562..7bcc2c5 100644 --- a/sdk.go +++ b/sdk.go @@ -18,6 +18,10 @@ type SDK struct { *NotificationsClient *NotificationLogsClient *NotificationSettingReplaysClient + *SimulationTypesClient + *SimulationsClient + *SimulationRunsClient + *SimulationRunEventsClient *IPAddressesClient *DiscountsClient *SubscriptionsClient @@ -43,6 +47,10 @@ func newSDK(d Doer) *SDK { PricingPreviewClient: &PricingPreviewClient{doer: d}, ProductsClient: &ProductsClient{doer: d}, ReportsClient: &ReportsClient{doer: d}, + SimulationRunEventsClient: &SimulationRunEventsClient{doer: d}, + SimulationRunsClient: &SimulationRunsClient{doer: d}, + SimulationTypesClient: &SimulationTypesClient{doer: d}, + SimulationsClient: &SimulationsClient{doer: d}, SubscriptionsClient: &SubscriptionsClient{doer: d}, TransactionsClient: &TransactionsClient{doer: d}, } diff --git a/shared.go b/shared.go index 9123a47..0af7b83 100644 --- a/shared.go +++ b/shared.go @@ -166,7 +166,7 @@ var ErrReceiptDataNotEnabled = &paddleerr.Error{ } // ErrRequestHeadersTooLarge represents a `request_headers_too_large` error. -// See https://developer.paddle.com/errors/apigateway/request_headers_too_large for more information. +// See https://developer.paddle.com/errors/shared/request_headers_too_large for more information. var ErrRequestHeadersTooLarge = &paddleerr.Error{ Code: "request_headers_too_large", Type: paddleerr.ErrorTypeRequestError, @@ -195,6 +195,9 @@ const ( TaxCategoryWebsiteHosting TaxCategory = "website-hosting" ) +// CustomData: Your own structured key-value data. +type CustomData map[string]any + // Status: Whether this entity can be used in Paddle.. type Status string @@ -1425,4 +1428,109 @@ type EventType struct { // AvailableVersions: List of API versions that this event type supports. AvailableVersions []int `json:"available_versions,omitempty"` } -type CustomData map[string]any + +// Data: New or changed entity. +type Data map[string]any + +// SimulationTypeName: Single event sent for this simulation, in the format `entity.event_type`.. +type SimulationTypeName string + +const ( + SimulationTypeNameAddressCreated SimulationTypeName = "address.created" + SimulationTypeNameAddressImported SimulationTypeName = "address.imported" + SimulationTypeNameAddressUpdated SimulationTypeName = "address.updated" + SimulationTypeNameAdjustmentCreated SimulationTypeName = "adjustment.created" + SimulationTypeNameAdjustmentUpdated SimulationTypeName = "adjustment.updated" + SimulationTypeNameBusinessCreated SimulationTypeName = "business.created" + SimulationTypeNameBusinessImported SimulationTypeName = "business.imported" + SimulationTypeNameBusinessUpdated SimulationTypeName = "business.updated" + SimulationTypeNameCustomerCreated SimulationTypeName = "customer.created" + SimulationTypeNameCustomerImported SimulationTypeName = "customer.imported" + SimulationTypeNameCustomerUpdated SimulationTypeName = "customer.updated" + SimulationTypeNameDiscountCreated SimulationTypeName = "discount.created" + SimulationTypeNameDiscountImported SimulationTypeName = "discount.imported" + SimulationTypeNameDiscountUpdated SimulationTypeName = "discount.updated" + SimulationTypeNamePayoutCreated SimulationTypeName = "payout.created" + SimulationTypeNamePayoutPaid SimulationTypeName = "payout.paid" + SimulationTypeNamePriceCreated SimulationTypeName = "price.created" + SimulationTypeNamePriceImported SimulationTypeName = "price.imported" + SimulationTypeNamePriceUpdated SimulationTypeName = "price.updated" + SimulationTypeNameProductCreated SimulationTypeName = "product.created" + SimulationTypeNameProductImported SimulationTypeName = "product.imported" + SimulationTypeNameProductUpdated SimulationTypeName = "product.updated" + SimulationTypeNameReportCreated SimulationTypeName = "report.created" + SimulationTypeNameReportUpdated SimulationTypeName = "report.updated" + SimulationTypeNameSubscriptionActivated SimulationTypeName = "subscription.activated" + SimulationTypeNameSubscriptionCanceled SimulationTypeName = "subscription.canceled" + SimulationTypeNameSubscriptionCreated SimulationTypeName = "subscription.created" + SimulationTypeNameSubscriptionImported SimulationTypeName = "subscription.imported" + SimulationTypeNameSubscriptionPastDue SimulationTypeName = "subscription.past_due" + SimulationTypeNameSubscriptionPaused SimulationTypeName = "subscription.paused" + SimulationTypeNameSubscriptionResumed SimulationTypeName = "subscription.resumed" + SimulationTypeNameSubscriptionTrialing SimulationTypeName = "subscription.trialing" + SimulationTypeNameSubscriptionUpdated SimulationTypeName = "subscription.updated" + SimulationTypeNameTransactionBilled SimulationTypeName = "transaction.billed" + SimulationTypeNameTransactionCanceled SimulationTypeName = "transaction.canceled" + SimulationTypeNameTransactionCompleted SimulationTypeName = "transaction.completed" + SimulationTypeNameTransactionCreated SimulationTypeName = "transaction.created" + SimulationTypeNameTransactionPaid SimulationTypeName = "transaction.paid" + SimulationTypeNameTransactionPastDue SimulationTypeName = "transaction.past_due" + SimulationTypeNameTransactionPaymentFailed SimulationTypeName = "transaction.payment_failed" + SimulationTypeNameTransactionReady SimulationTypeName = "transaction.ready" + SimulationTypeNameTransactionUpdated SimulationTypeName = "transaction.updated" + SimulationTypeNameSubscriptionCreation SimulationTypeName = "subscription_creation" + SimulationTypeNameSubscriptionRenewal SimulationTypeName = "subscription_renewal" + SimulationTypeNameSubscriptionPause SimulationTypeName = "subscription_pause" + SimulationTypeNameSubscriptionResume SimulationTypeName = "subscription_resume" + SimulationTypeNameSubscriptionCancellation SimulationTypeName = "subscription_cancellation" +) + +// Payload: Simulation payload. `null` for scenarios. +type Payload map[string]any + +// SimulationEventStatus: Status of this simulation run log.. +type SimulationEventStatus string + +const ( + SimulationEventStatusPending SimulationEventStatus = "pending" + SimulationEventStatusSuccess SimulationEventStatus = "success" + SimulationEventStatusFailed SimulationEventStatus = "failed" + SimulationEventStatusAborted SimulationEventStatus = "aborted" +) + +// SimulationEventRequest: Information about the request. Sent by Paddle as part of the simulation. +type SimulationEventRequest struct { + // Body: Request body sent by Paddle. + Body string `json:"body,omitempty"` +} + +// SimulationEventResponse: Information about the response. Sent by the responding server for the notification setting. +type SimulationEventResponse struct { + // Body: Response body sent by the responding server. May be empty for success responses. + Body string `json:"body,omitempty"` + // StatusCode: HTTP status code sent by the responding server. + StatusCode int `json:"status_code,omitempty"` +} + +/* +SimulationEvent: Events associated with this simulation run. Paddle creates a list of events for each simulation runs. Returned when the +`include` parameter is used with the `events` value. +*/ +type SimulationEvent struct { + // ID: Unique Paddle ID for this simulation event, prefixed with `ntfsimevt_`. + ID string `json:"id,omitempty"` + // Status: Status of this simulation run log. + Status SimulationEventStatus `json:"status,omitempty"` + // EventType: Type of event sent by Paddle, in the format `entity.event_type`. + EventType EventTypeName `json:"event_type,omitempty"` + // Payload: Simulation payload. Pass a JSON object that matches the schema for an event type to simulate a custom payload. If omitted, Paddle populates with a demo example. + Payload Payload `json:"payload,omitempty"` + // Request: Information about the request. Sent by Paddle as part of the simulation. + Request *SimulationEventRequest `json:"request,omitempty"` + // Response: Information about the response. Sent by the responding server for the notification setting. + Response *SimulationEventResponse `json:"response,omitempty"` + // CreatedAt: RFC 3339 datetime string of when this entity was created. Set automatically by Paddle. + CreatedAt string `json:"created_at,omitempty"` + // UpdatedAt: RFC 3339 datetime string of when this entity was updated. Set automatically by Paddle. + UpdatedAt string `json:"updated_at,omitempty"` +} diff --git a/simulation_run_events.go b/simulation_run_events.go new file mode 100644 index 0000000..73594eb --- /dev/null +++ b/simulation_run_events.go @@ -0,0 +1,81 @@ +// Code generated by the Paddle SDK Generator; DO NOT EDIT. + +package paddle + +import "context" + +// SimulationRunEventsClient is a client for the Simulation run events resource. +type SimulationRunEventsClient struct { + doer Doer +} + +// ListSimulationsEventsRequest is given as an input to ListSimulationsEvents. +type ListSimulationsEventsRequest struct { + // URL path parameters. + SimulationID string `in:"path=simulation_id" json:"-"` + SimulationRunID string `in:"path=simulation_run_id" json:"-"` + + // After is a query parameter. + // Return entities after the specified Paddle ID when working with paginated endpoints. Used in the `meta.pagination.next` URL in responses for list operations. + After *string `in:"query=after;omitempty" json:"-"` + // OrderBy is a query parameter. + /* + Order returned entities by the specified field and direction (`[ASC]` or `[DESC]`). For example, `?order_by=id[ASC]`. + + Valid fields for ordering: `id`. + */ + OrderBy *string `in:"query=order_by;omitempty" json:"-"` + // PerPage is a query parameter. + /* + Set how many entities are returned per page. Paddle returns the maximum number of results if a number greater than the maximum is requested. Check `meta.pagination.per_page` in the response to see how many were returned. + + Default: `50`; Maximum: `200`. + */ + PerPage *int `in:"query=per_page;omitempty" json:"-"` + // ID is a query parameter. + // Return only the IDs specified. Use a comma-separated list to get multiple entities. + ID []string `in:"query=id;omitempty" json:"-"` +} + +// ListSimulationsEvents performs the GET operation on a Simulation run events resource. +func (c *SimulationRunEventsClient) ListSimulationsEvents(ctx context.Context, req *ListSimulationsEventsRequest) (res *Collection[*SimulationEvent], err error) { + if err := c.doer.Do(ctx, "GET", "/simulations/{simulation_id}/runs/{simulation_run_id}/events", req, &res); err != nil { + return nil, err + } + + return res, nil +} + +// GetSimulationEventRequest is given as an input to GetSimulationEvent. +type GetSimulationEventRequest struct { + // URL path parameters. + SimulationID string `in:"path=simulation_id" json:"-"` + SimulationRunID string `in:"path=simulation_run_id" json:"-"` + SimulationEventID string `in:"path=simulation_event_id" json:"-"` +} + +// GetSimulationEvent performs the GET operation on a Simulation run events resource. +func (c *SimulationRunEventsClient) GetSimulationEvent(ctx context.Context, req *GetSimulationEventRequest) (res *SimulationEvent, err error) { + if err := c.doer.Do(ctx, "GET", "/simulations/{simulation_id}/runs/{simulation_run_id}/events/{simulation_event_id}", req, &res); err != nil { + return nil, err + } + + return res, nil +} + +// ReplaySimulationRunEventRequest is given as an input to ReplaySimulationRunEvent. +type ReplaySimulationRunEventRequest struct { + // URL path parameters. + SimulationID string `in:"path=simulation_id" json:"-"` + SimulationRunID string `in:"path=simulation_run_id" json:"-"` + SimulationEventID string `in:"path=simulation_event_id" json:"-"` +} + +// ReplaySimulationRunEvent performs the POST operation on a Simulation run events resource. +func (c *SimulationRunEventsClient) ReplaySimulationRunEvent(ctx context.Context, req *ReplaySimulationRunEventRequest) (err error) { + if err := c.doer.Do(ctx, "POST", "/simulations/{simulation_id}/runs/{simulation_run_id}/events/{simulation_event_id}/replay", req, nil); err != nil { + return err + } + + return nil +} diff --git a/simulation_runs.go b/simulation_runs.go new file mode 100644 index 0000000..605a277 --- /dev/null +++ b/simulation_runs.go @@ -0,0 +1,113 @@ +// Code generated by the Paddle SDK Generator; DO NOT EDIT. + +package paddle + +import "context" + +// SimulationRunStatus: Status of this simulation run.. +type SimulationRunStatus string + +const ( + SimulationRunStatusPending SimulationRunStatus = "pending" + SimulationRunStatusCompleted SimulationRunStatus = "completed" + SimulationRunStatusCanceled SimulationRunStatus = "canceled" +) + +// SimulationRun: Represents a simulation run entity. +type SimulationRun struct { + // ID: Unique Paddle ID for this simulation run, prefixed with `ntfsimrun_`. + ID string `json:"id,omitempty"` + // Status: Status of this simulation run. + Status SimulationRunStatus `json:"status,omitempty"` + // CreatedAt: RFC 3339 datetime string of when this entity was created. Set automatically by Paddle. + CreatedAt string `json:"created_at,omitempty"` + // UpdatedAt: RFC 3339 datetime string of when this entity was updated. Set automatically by Paddle. + UpdatedAt string `json:"updated_at,omitempty"` + // Type: Single event sent for this simulation, in the format `entity.event_type`. + Type SimulationTypeName `json:"type,omitempty"` + /* + Events: Events associated with this simulation run. Paddle creates a list of events for each simulation runs. Returned when the + `include` parameter is used with the `events` value. + */ + Events []SimulationEvent `json:"events,omitempty"` +} + +// SimulationRunsClient is a client for the Simulation runs resource. +type SimulationRunsClient struct { + doer Doer +} + +// ListSimulationRunsRequest is given as an input to ListSimulationRuns. +type ListSimulationRunsRequest struct { + // URL path parameters. + SimulationID string `in:"path=simulation_id" json:"-"` + + // After is a query parameter. + // Return entities after the specified Paddle ID when working with paginated endpoints. Used in the `meta.pagination.next` URL in responses for list operations. + After *string `in:"query=after;omitempty" json:"-"` + // OrderBy is a query parameter. + /* + Order returned entities by the specified field and direction (`[ASC]` or `[DESC]`). For example, `?order_by=id[ASC]`. + + Valid fields for ordering: `id`. + */ + OrderBy *string `in:"query=order_by;omitempty" json:"-"` + // PerPage is a query parameter. + /* + Set how many entities are returned per page. Paddle returns the maximum number of results if a number greater than the maximum is requested. Check `meta.pagination.per_page` in the response to see how many were returned. + + Default: `50`; Maximum: `200`. + */ + PerPage *int `in:"query=per_page;omitempty" json:"-"` + // ID is a query parameter. + // Return only the IDs specified. Use a comma-separated list to get multiple entities. + ID []string `in:"query=id;omitempty" json:"-"` + + // IncludeEvents allows requesting the events sub-resource as part of this request. + // If set to true, will be included on the response. + IncludeEvents bool `in:"paddle_include=events" json:"-"` +} + +// ListSimulationRuns performs the GET operation on a Simulation runs resource. +func (c *SimulationRunsClient) ListSimulationRuns(ctx context.Context, req *ListSimulationRunsRequest) (res *Collection[*SimulationRun], err error) { + if err := c.doer.Do(ctx, "GET", "/simulations/{simulation_id}/runs", req, &res); err != nil { + return nil, err + } + + return res, nil +} + +// CreateSimulationRunRequest is given as an input to CreateSimulationRun. +type CreateSimulationRunRequest struct { + // URL path parameters. + SimulationID string `in:"path=simulation_id" json:"-"` +} + +// CreateSimulationRun performs the POST operation on a Simulation runs resource. +func (c *SimulationRunsClient) CreateSimulationRun(ctx context.Context, req *CreateSimulationRunRequest) (res *SimulationRun, err error) { + if err := c.doer.Do(ctx, "POST", "/simulations/{simulation_id}/runs", req, &res); err != nil { + return nil, err + } + + return res, nil +} + +// GetSimulationRunRequest is given as an input to GetSimulationRun. +type GetSimulationRunRequest struct { + // URL path parameters. + SimulationID string `in:"path=simulation_id" json:"-"` + SimulationRunID string `in:"path=simulation_run_id" json:"-"` + + // IncludeEvents allows requesting the events sub-resource as part of this request. + // If set to true, will be included on the response. + IncludeEvents bool `in:"paddle_include=events" json:"-"` +} + +// GetSimulationRun performs the GET operation on a Simulation runs resource. +func (c *SimulationRunsClient) GetSimulationRun(ctx context.Context, req *GetSimulationRunRequest) (res *SimulationRun, err error) { + if err := c.doer.Do(ctx, "GET", "/simulations/{simulation_id}/runs/{simulation_run_id}", req, &res); err != nil { + return nil, err + } + + return res, nil +} diff --git a/simulation_types.go b/simulation_types.go new file mode 100644 index 0000000..a1f91a1 --- /dev/null +++ b/simulation_types.go @@ -0,0 +1,46 @@ +// Code generated by the Paddle SDK Generator; DO NOT EDIT. + +package paddle + +import "context" + +// SimulationKind: Type of simulation.. +type SimulationKind string + +const ( + SimulationKindSingleEvent SimulationKind = "single_event" + SimulationKindScenario SimulationKind = "scenario" +) + +// SimulationType: Represents a simulation type. +type SimulationType struct { + // Name: Type of simulation sent by Paddle. Single event simulations are in the format `entity.event_type`; scenario simulations are in `snake_case`. + Name string `json:"name,omitempty"` + // Label: Descriptive label for this simulation type. Typically gives more context about a scenario. Single event simulations are in the format `entity.event_type`. + Label string `json:"label,omitempty"` + // Description: Short description of this simulation type. + Description string `json:"description,omitempty"` + // Group: Group for this simulation type. Typically the entity that this event relates to. + Group string `json:"group,omitempty"` + // Type: Type of simulation. + Type SimulationKind `json:"type,omitempty"` + // Events: Type of event sent by Paddle, in the format `entity.event_type`. + Events []EventTypeName `json:"events,omitempty"` +} + +// SimulationTypesClient is a client for the Simulation types resource. +type SimulationTypesClient struct { + doer Doer +} + +// ListSimulationTypesRequest is given as an input to ListSimulationTypes. +type ListSimulationTypesRequest struct{} + +// ListSimulationTypes performs the GET operation on a Simulation types resource. +func (c *SimulationTypesClient) ListSimulationTypes(ctx context.Context, req *ListSimulationTypesRequest) (res *Collection[*SimulationType], err error) { + if err := c.doer.Do(ctx, "GET", "/simulation-types", req, &res); err != nil { + return nil, err + } + + return res, nil +} diff --git a/simulations.go b/simulations.go new file mode 100644 index 0000000..fd1a74f --- /dev/null +++ b/simulations.go @@ -0,0 +1,247 @@ +// Code generated by the Paddle SDK Generator; DO NOT EDIT. + +package paddle + +import ( + "context" + "encoding/json" +) + +// Simulation: Represents a simulation entity. +type Simulation struct { + // ID: Unique Paddle ID for this simulation, prefixed with `ntfsim_`. + ID string `json:"id,omitempty"` + // Status: Whether this entity can be used in Paddle. + Status Status `json:"status,omitempty"` + // NotificationSettingID: Paddle ID of the notification setting where this simulation is sent, prefixed with `ntfset_`. + NotificationSettingID string `json:"notification_setting_id,omitempty"` + // Name: Name of this simulation. + Name string `json:"name,omitempty"` + // Type: Single event sent for this simulation, in the format `entity.event_type`. + Type SimulationTypeName `json:"type,omitempty"` + // Payload: Simulation payload. `null` for scenarios. + Payload *Payload `json:"payload,omitempty"` + // LastRunAt: RFC 3339 datetime string of when this simulation was last run. `null` until run. Set automatically by Paddle. + LastRunAt *string `json:"last_run_at,omitempty"` + // CreatedAt: RFC 3339 datetime string of when this entity was created. Set automatically by Paddle. + CreatedAt string `json:"created_at,omitempty"` + // UpdatedAt: RFC 3339 datetime string of when this entity was updated. Set automatically by Paddle. + UpdatedAt string `json:"updated_at,omitempty"` +} + +// SimulationSingleEventCreate: Single event simulations play a single event. +type SimulationSingleEventCreate struct { + // NotificationSettingID: Paddle ID of the notification setting where this simulation is sent, prefixed with `ntfset_`. + NotificationSettingID string `json:"notification_setting_id,omitempty"` + // Name: Name of this simulation. + Name string `json:"name,omitempty"` + // Type: Single event sent for this simulation, in the format `entity.event_type`. + Type EventTypeName `json:"type,omitempty"` + // Payload: Simulation payload. Pass a JSON object that matches the schema for an event type to simulate a custom payload. If omitted, Paddle populates with a demo example. + Payload *Payload `json:"payload,omitempty"` +} + +// SimulationScenarioType: Scenario for this simulation. Scenario simulations play all events sent for a subscription lifecycle event.. +type SimulationScenarioType string + +const ( + SimulationScenarioTypeSubscriptionCreation SimulationScenarioType = "subscription_creation" + SimulationScenarioTypeSubscriptionRenewal SimulationScenarioType = "subscription_renewal" + SimulationScenarioTypeSubscriptionPause SimulationScenarioType = "subscription_pause" + SimulationScenarioTypeSubscriptionResume SimulationScenarioType = "subscription_resume" + SimulationScenarioTypeSubscriptionCancellation SimulationScenarioType = "subscription_cancellation" +) + +// SimulationScenarioCreate: Scenario simulations play all events sent for a subscription lifecycle event. +type SimulationScenarioCreate struct { + // NotificationSettingID: Paddle ID of the notification setting where this simulation is sent, prefixed with `ntfset_`. + NotificationSettingID string `json:"notification_setting_id,omitempty"` + // Name: Name of this simulation. + Name string `json:"name,omitempty"` + // Type: Scenario for this simulation. Scenario simulations play all events sent for a subscription lifecycle event. + Type SimulationScenarioType `json:"type,omitempty"` +} + +// SimulationSingleEventUpdate: Single event simulations play a single event. +type SimulationSingleEventUpdate struct { + // NotificationSettingID: Paddle ID of the notification setting where this simulation is sent, prefixed with `ntfset_`. + NotificationSettingID string `json:"notification_setting_id,omitempty"` + // Name: Name of this simulation. + Name string `json:"name,omitempty"` + // Status: Whether this entity can be used in Paddle. + Status Status `json:"status,omitempty"` + // Type: Single event sent for this simulation, in the format `entity.event_type`. + Type EventTypeName `json:"type,omitempty"` + // Payload: Simulation payload. Pass a JSON object that matches the schema for an event type to simulate a custom payload. Set to `null` to clear and populate with a demo example. + Payload *Payload `json:"payload,omitempty"` +} + +// SimulationScenarioUpdate: Scenario simulations play all events sent for a subscription lifecycle event. +type SimulationScenarioUpdate struct { + // NotificationSettingID: Paddle ID of the notification setting where this simulation is sent, prefixed with `ntfset_`. + NotificationSettingID string `json:"notification_setting_id,omitempty"` + // Name: Name of this simulation. + Name string `json:"name,omitempty"` + // Status: Whether this entity can be used in Paddle. + Status Status `json:"status,omitempty"` + // Type: Scenario for this simulation. Scenario simulations play all events sent for a subscription lifecycle event. + Type SimulationScenarioType `json:"type,omitempty"` +} + +// SimulationsClient is a client for the Simulations resource. +type SimulationsClient struct { + doer Doer +} + +// ListSimulationsRequest is given as an input to ListSimulations. +type ListSimulationsRequest struct { + // After is a query parameter. + // Return entities after the specified Paddle ID when working with paginated endpoints. Used in the `meta.pagination.next` URL in responses for list operations. + After *string `in:"query=after;omitempty" json:"-"` + // NotificationSettingID is a query parameter. + // Return entities related to the specified notification destination. Use a comma-separated list to specify multiple notification destination IDs. + NotificationSettingID []string `in:"query=notification_setting_id;omitempty" json:"-"` + // OrderBy is a query parameter. + /* + Order returned entities by the specified field and direction (`[ASC]` or `[DESC]`). For example, `?order_by=id[ASC]`. + + Valid fields for ordering: `id`. + */ + OrderBy *string `in:"query=order_by;omitempty" json:"-"` + // PerPage is a query parameter. + /* + Set how many entities are returned per page. Paddle returns the maximum number of results if a number greater than the maximum is requested. Check `meta.pagination.per_page` in the response to see how many were returned. + + Default: `50`; Maximum: `200`. + */ + PerPage *int `in:"query=per_page;omitempty" json:"-"` + // ID is a query parameter. + // Return only the IDs specified. Use a comma-separated list to get multiple entities. + ID []string `in:"query=id;omitempty" json:"-"` + // Status is a query parameter. + // Return entities that match the specified status. Use a comma-separated list to specify multiple status values. + Status []string `in:"query=status;omitempty" json:"-"` +} + +// ListSimulations performs the GET operation on a Simulations resource. +func (c *SimulationsClient) ListSimulations(ctx context.Context, req *ListSimulationsRequest) (res *Collection[*Simulation], err error) { + if err := c.doer.Do(ctx, "GET", "/simulations", req, &res); err != nil { + return nil, err + } + + return res, nil +} + +// NewCreateSimulationRequestSimulationSingleEventCreate takes a SimulationSingleEventCreate type +// and creates a CreateSimulationRequest for use in a request. +func NewCreateSimulationRequestSimulationSingleEventCreate(r *SimulationSingleEventCreate) *CreateSimulationRequest { + return &CreateSimulationRequest{SimulationSingleEventCreate: r} +} + +// NewCreateSimulationRequestSimulationScenarioCreate takes a SimulationScenarioCreate type +// and creates a CreateSimulationRequest for use in a request. +func NewCreateSimulationRequestSimulationScenarioCreate(r *SimulationScenarioCreate) *CreateSimulationRequest { + return &CreateSimulationRequest{SimulationScenarioCreate: r} +} + +// CreateSimulationRequest represents a union request type of the following types: +// - `SimulationSingleEventCreate` +// - `SimulationScenarioCreate` +// +// The following constructor functions can be used to create a new instance of this type. +// - `NewCreateSimulationRequestSimulationSingleEventCreate()` +// - `NewCreateSimulationRequestSimulationScenarioCreate()` +// +// Only one of the values can be set at a time, the first non-nil value will be used in the request. +type CreateSimulationRequest struct { + *SimulationSingleEventCreate + *SimulationScenarioCreate +} + +// CreateSimulation performs the POST operation on a Simulations resource. +func (c *SimulationsClient) CreateSimulation(ctx context.Context, req *CreateSimulationRequest) (res *Simulation, err error) { + if err := c.doer.Do(ctx, "POST", "/simulations", req, &res); err != nil { + return nil, err + } + + return res, nil +} + +// MarshalJSON implements the json.Marshaler interface. +func (u CreateSimulationRequest) MarshalJSON() ([]byte, error) { + if u.SimulationSingleEventCreate != nil { + return json.Marshal(u.SimulationSingleEventCreate) + } + + if u.SimulationScenarioCreate != nil { + return json.Marshal(u.SimulationScenarioCreate) + } + + return nil, nil +} + +// GetSimulationRequest is given as an input to GetSimulation. +type GetSimulationRequest struct { + // URL path parameters. + SimulationID string `in:"path=simulation_id" json:"-"` +} + +// GetSimulation performs the GET operation on a Simulations resource. +func (c *SimulationsClient) GetSimulation(ctx context.Context, req *GetSimulationRequest) (res *Simulation, err error) { + if err := c.doer.Do(ctx, "GET", "/simulations/{simulation_id}", req, &res); err != nil { + return nil, err + } + + return res, nil +} + +// NewUpdateSimulationRequestSimulationSingleEventUpdate takes a SimulationSingleEventUpdate type +// and creates a UpdateSimulationRequest for use in a request. +func NewUpdateSimulationRequestSimulationSingleEventUpdate(simulationID string, r *SimulationSingleEventUpdate) *UpdateSimulationRequest { + return &UpdateSimulationRequest{SimulationID: simulationID, SimulationSingleEventUpdate: r} +} + +// NewUpdateSimulationRequestSimulationScenarioUpdate takes a SimulationScenarioUpdate type +// and creates a UpdateSimulationRequest for use in a request. +func NewUpdateSimulationRequestSimulationScenarioUpdate(simulationID string, r *SimulationScenarioUpdate) *UpdateSimulationRequest { + return &UpdateSimulationRequest{SimulationID: simulationID, SimulationScenarioUpdate: r} +} + +// UpdateSimulationRequest represents a union request type of the following types: +// - `SimulationSingleEventUpdate` +// - `SimulationScenarioUpdate` +// +// The following constructor functions can be used to create a new instance of this type. +// - `NewUpdateSimulationRequestSimulationSingleEventUpdate()` +// - `NewUpdateSimulationRequestSimulationScenarioUpdate()` +// +// Only one of the values can be set at a time, the first non-nil value will be used in the request. +type UpdateSimulationRequest struct { + // URL path parameters. + SimulationID string `in:"path=simulation_id" json:"-"` + + *SimulationSingleEventUpdate + *SimulationScenarioUpdate +} + +// UpdateSimulation performs the PATCH operation on a Simulations resource. +func (c *SimulationsClient) UpdateSimulation(ctx context.Context, req *UpdateSimulationRequest) (res *Simulation, err error) { + if err := c.doer.Do(ctx, "PATCH", "/simulations/{simulation_id}", req, &res); err != nil { + return nil, err + } + + return res, nil +} + +// MarshalJSON implements the json.Marshaler interface. +func (u UpdateSimulationRequest) MarshalJSON() ([]byte, error) { + if u.SimulationSingleEventUpdate != nil { + return json.Marshal(u.SimulationSingleEventUpdate) + } + + if u.SimulationScenarioUpdate != nil { + return json.Marshal(u.SimulationScenarioUpdate) + } + + return nil, nil +} diff --git a/subscriptions.go b/subscriptions.go index 0a289ec..cdda595 100644 --- a/subscriptions.go +++ b/subscriptions.go @@ -359,6 +359,13 @@ var ErrSubscriptionManualCollectionModeActivationNotAllowed = &paddleerr.Error{ Type: paddleerr.ErrorTypeRequestError, } +// ErrSubscriptionArchivedDiscountApplicationAttempt represents a `subscription_archived_discount_application_attempt` error. +// See https://developer.paddle.com/errors/subscriptions/subscription_archived_discount_application_attempt for more information. +var ErrSubscriptionArchivedDiscountApplicationAttempt = &paddleerr.Error{ + Code: "subscription_archived_discount_application_attempt", + Type: paddleerr.ErrorTypeRequestError, +} + // SubscriptionStatus: Status of this subscription. Set automatically by Paddle. Use the pause subscription or cancel subscription operations to change.. type SubscriptionStatus string diff --git a/transactions.go b/transactions.go index aa4e565..1c295d6 100644 --- a/transactions.go +++ b/transactions.go @@ -268,6 +268,13 @@ var ErrTransactionImmutableWhileProcessingPayment = &paddleerr.Error{ Type: paddleerr.ErrorTypeRequestError, } +// ErrTransactionCannotBeRevisedWithInvalidTaxIdentifier represents a `transaction_cannot_be_revised_with_invalid_tax_identifier` error. +// See https://developer.paddle.com/errors/transactions/transaction_cannot_be_revised_with_invalid_tax_identifier for more information. +var ErrTransactionCannotBeRevisedWithInvalidTaxIdentifier = &paddleerr.Error{ + Code: "transaction_cannot_be_revised_with_invalid_tax_identifier", + Type: paddleerr.ErrorTypeRequestError, +} + // AdjustmentTotalsBreakdown: Breakdown of the total adjustments by adjustment action. type AdjustmentTotalsBreakdown struct { // Credit: Total amount of credit adjustments. From 31e9190aa4f3729ff4de179c29073bb0a1c0f6fe Mon Sep 17 00:00:00 2001 From: Michael Woodward Date: Tue, 8 Oct 2024 14:40:00 +0100 Subject: [PATCH 2/9] fix: Subscription resume missing path param on union func --- subscriptions.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/subscriptions.go b/subscriptions.go index cdda595..f1c0ec2 100644 --- a/subscriptions.go +++ b/subscriptions.go @@ -1025,14 +1025,14 @@ func (c *SubscriptionsClient) PauseSubscription(ctx context.Context, req *PauseS // NewResumeSubscriptionRequestResumeOnASpecificDate takes a ResumeOnASpecificDate type // and creates a ResumeSubscriptionRequest for use in a request. -func NewResumeSubscriptionRequestResumeOnASpecificDate(r *ResumeOnASpecificDate) *ResumeSubscriptionRequest { - return &ResumeSubscriptionRequest{ResumeOnASpecificDate: r} +func NewResumeSubscriptionRequestResumeOnASpecificDate(subscriptionID string, r *ResumeOnASpecificDate) *ResumeSubscriptionRequest { + return &ResumeSubscriptionRequest{SubscriptionID: subscriptionID, ResumeOnASpecificDate: r} } // NewResumeSubscriptionRequestResumeImmediately takes a ResumeImmediately type // and creates a ResumeSubscriptionRequest for use in a request. -func NewResumeSubscriptionRequestResumeImmediately(r *ResumeImmediately) *ResumeSubscriptionRequest { - return &ResumeSubscriptionRequest{ResumeImmediately: r} +func NewResumeSubscriptionRequestResumeImmediately(subscriptionID string, r *ResumeImmediately) *ResumeSubscriptionRequest { + return &ResumeSubscriptionRequest{SubscriptionID: subscriptionID, ResumeImmediately: r} } // ResumeSubscriptionRequest represents a union request type of the following types: From 9daec4de078c84b617f3c33f344d03572547df3f Mon Sep 17 00:00:00 2001 From: Michael Woodward Date: Tue, 8 Oct 2024 16:18:31 +0100 Subject: [PATCH 3/9] feat: Support currency_code filtering on customer credit notes --- adjustments.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/adjustments.go b/adjustments.go index 099da83..d7d59fb 100644 --- a/adjustments.go +++ b/adjustments.go @@ -264,6 +264,10 @@ func (c *AdjustmentsClient) GetAdjustmentCreditNote(ctx context.Context, req *Ge type ListCreditBalancesRequest struct { // URL path parameters. CustomerID string `in:"path=customer_id" json:"-"` + + // CurrencyCode is a query parameter. + // Return entities that match the currency code. Use a comma-separated list to specify multiple currency codes. + CurrencyCode []string `in:"query=currency_code;omitempty" json:"-"` } // ListCreditBalances performs the GET operation on a Adjustments resource. From c4f5999b9163b44f976a1617acd090505fcefab9 Mon Sep 17 00:00:00 2001 From: Michael Woodward Date: Tue, 8 Oct 2024 16:19:09 +0100 Subject: [PATCH 4/9] docs(release): 2.1.0 --- CHANGELOG.md | 14 ++++++++++++++ internal/client/version.txt | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9bcf8e..8587eff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), Check our main [developer changelog](https://developer.paddle.com/?utm_source=dx&utm_medium=paddle-go-sdk) for information about changes to the Paddle Billing platform, the Paddle API, and other developer tools. +## 2.1.0 - 2024-10-08 + +### Added + +- Support for the Simulation API - see related [changelog entry](https://developer.paddle.com/changelog/2024/webhook-simulator) +- Filtering of customer credit balances by `currency_code` in `AdjustmentsClient.ListCreditBalances` +- New API error mappings + +### Fixed + +- Subscription resume union funcs now accept the required path param + - `NewResumeSubscriptionRequestResumeOnASpecificDate` + - `NewResumeSubscriptionRequestResumeImmediately` + ## 2.0.0 - 2024-09-18 ### Changed diff --git a/internal/client/version.txt b/internal/client/version.txt index 227cea2..7ec1d6d 100644 --- a/internal/client/version.txt +++ b/internal/client/version.txt @@ -1 +1 @@ -2.0.0 +2.1.0 From 4fea02281b6e90afdb060dd97e5a69a680b084c5 Mon Sep 17 00:00:00 2001 From: Michael Woodward Date: Tue, 8 Oct 2024 18:19:32 +0100 Subject: [PATCH 5/9] chore(ci): Drop coverage checks The SDK is generated and provides testable examples, there is little benefit to having a coverage criteria set. --- .github/workflows/go.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index f0c296f..d35eecd 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -40,10 +40,4 @@ jobs: run: go vet ./... - name: Test - run: go test ./... -v -coverprofile=./cover.out -covermode atomic -coverpkg ./... -race - - - name: Check coverage - uses: vladopajic/go-test-coverage@v2 - with: - profile: cover.out - threshold-total: 30 + run: go test ./... -v From 38c8b1848a01ae1d81e5925f9871a64f7efad01c Mon Sep 17 00:00:00 2001 From: Michael Woodward Date: Fri, 11 Oct 2024 23:10:23 +0100 Subject: [PATCH 6/9] feat: Support re-use of notification payloads in Simulator API fix: Fixes unmarshal for GetNotification --- CHANGELOG.md | 3 +- collection.go | 10 +- events.go | 2 +- example_simulations_test.go | 298 +++++++++++++++++++++++ example_test.go | 4 + internal/response/response.go | 7 +- pkg/paddlenotification/addresses.go | 30 +-- pkg/paddlenotification/adjustments.go | 32 +-- pkg/paddlenotification/businesses.go | 24 +- pkg/paddlenotification/customers.go | 22 +- pkg/paddlenotification/discounts.go | 36 +-- pkg/paddlenotification/notification.go | 4 +- pkg/paddlenotification/payouts.go | 10 +- pkg/paddlenotification/prices.go | 34 +-- pkg/paddlenotification/products.go | 24 +- pkg/paddlenotification/reports.go | 18 +- pkg/paddlenotification/subscriptions.go | 48 ++-- pkg/paddlenotification/transactions.go | 46 ++-- shared.go | 62 ++++- simulations.go | 63 ++++- testdata/simulation.json | 27 ++ testdata/simulation_run.json | 12 + testdata/simulation_run_with_events.json | 38 +++ testdata/simulations.json | 61 +++++ 24 files changed, 750 insertions(+), 165 deletions(-) create mode 100644 example_simulations_test.go create mode 100644 testdata/simulation.json create mode 100644 testdata/simulation_run.json create mode 100644 testdata/simulation_run_with_events.json create mode 100644 testdata/simulations.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 8587eff..efb61f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), Check our main [developer changelog](https://developer.paddle.com/?utm_source=dx&utm_medium=paddle-go-sdk) for information about changes to the Paddle Billing platform, the Paddle API, and other developer tools. -## 2.1.0 - 2024-10-08 +## 2.1.0 - 2024-10-15 ### Added @@ -19,6 +19,7 @@ Check our main [developer changelog](https://developer.paddle.com/?utm_source=dx - Subscription resume union funcs now accept the required path param - `NewResumeSubscriptionRequestResumeOnASpecificDate` - `NewResumeSubscriptionRequestResumeImmediately` +- Response unmarshal for GetNotifications to the Notification struct ## 2.0.0 - 2024-09-18 diff --git a/collection.go b/collection.go index 9170f1b..9d2d19f 100644 --- a/collection.go +++ b/collection.go @@ -100,11 +100,12 @@ func (c *Collection[T]) Next(ctx context.Context) *Res[T] { return &ptrCopy } -// Ensure that Collection[T] implements the json.Unmarshaler and the +// Ensure that Collection[T] implements the response.Unmarshaler and the // client.Wanter interfaces. var ( - _ json.Unmarshaler = &Collection[any]{} - _ client.Wanter = &Collection[any]{} + _ response.UnmarshalsResponses = &Collection[any]{} + _ json.Unmarshaler = &Collection[any]{} + _ client.Wanter = &Collection[any]{} ) // Wants sets the client to be used for making requests. @@ -157,6 +158,9 @@ func (c *Collection[T]) IterErr(ctx context.Context, fn func(v T) error) error { } } +// UnmarshalsResponses acts as a marker to identify this struct must Unmarshal the entire response. +func (c *Collection[T]) UnmarshalsResponses() {} + // UnmarshalJSON unmarshals the collection from a JSON response. func (c *Collection[T]) UnmarshalJSON(b []byte) error { if len(b) == 0 { diff --git a/events.go b/events.go index 1976ec9..ffffe0c 100644 --- a/events.go +++ b/events.go @@ -22,7 +22,7 @@ type GenericEvent struct { // OccurredAt: RFC 3339 datetime string of when this event occurred. OccurredAt string `json:"occurred_at,omitempty"` // Data: New or changed entity. - Data any `json:"data,omitempty"` + Data paddlenotification.NotificationPayload `json:"data,omitempty"` } // AddressCreatedEvent represents an Event implementation for address.created event. diff --git a/example_simulations_test.go b/example_simulations_test.go new file mode 100644 index 0000000..87526db --- /dev/null +++ b/example_simulations_test.go @@ -0,0 +1,298 @@ +package paddle_test + +import ( + "context" + "fmt" + "os" + "time" + + paddle "github.com/PaddleHQ/paddle-go-sdk" + "github.com/PaddleHQ/paddle-go-sdk/pkg/paddlenotification" +) + +// Demonstrates how to create a Simulation with Payload and read the Payload back out of the response +func Example_simulation_create() { + // Create a mock HTTP server for this example - skip over this bit! + s := mockServerForExample(mockServerResponse{stub: &stub{paths: []stubPath{simulation}}}) + + // Create a new Paddle client. + client, err := paddle.New( + os.Getenv("PADDLE_API_KEY"), + paddle.WithBaseURL(s.URL), // Uses the mock server, you will not need this in your integration. + ) + + if err != nil { + fmt.Println(err) + return + } + + ctx := context.Background() + + // Optionally set a transit ID on the context. This is useful to link your + // own request IDs to Paddle API requests. + ctx = paddle.ContextWithTransitID(ctx, "sdk-testing-request-1") + + simulation, err := client.CreateSimulation(ctx, paddle.NewCreateSimulationRequestSimulationSingleEventCreate(&paddle.SimulationSingleEventCreate{ + NotificationSettingID: "ntfset_01j84xydheq48n3btebwf6ndn6", + Name: "Go SDK Test with Payload", + Type: "customer.created", + Payload: paddlenotification.CustomerNotification{ + ID: "ctm_01j870snka0xdp6szgyxze6d6d", + Name: paddle.PtrTo("John Doe"), + Email: "john.doe@paddle.com", + MarketingConsent: false, + Status: paddlenotification.StatusActive, + CustomData: nil, + Locale: "en", + CreatedAt: time.Date(2024, 4, 12, 0, 0, 0, 0, time.UTC).Format(time.RFC3339), + UpdatedAt: time.Date(2024, 4, 12, 0, 0, 0, 0, time.UTC).Format(time.RFC3339), + ImportMeta: nil, + }, + })) + if err != nil { + return + } + + // Use type assertion on the response for payload + payload, ok := (simulation.Payload).(*paddlenotification.CustomerNotification) + if !ok { + fmt.Println("Payload is unexpected type") + return + } + + fmt.Println(simulation.ID) + fmt.Println(payload.ID) + // Output: + //ntfsim_01j9y0jwekrcyezscgkehvdmd6 + //ctm_01j870snka0xdp6szgyxze6d6d +} + +// Demonstrates how to update a Simulation with Payload and read the Payload back out of the response +func Example_simulation_update() { + // Create a mock HTTP server for this example - skip over this bit! + s := mockServerForExample(mockServerResponse{stub: &stub{paths: []stubPath{simulation}}}) + + // Create a new Paddle client. + client, err := paddle.New( + os.Getenv("PADDLE_API_KEY"), + paddle.WithBaseURL(s.URL), // Uses the mock server, you will not need this in your integration. + ) + + if err != nil { + fmt.Println(err) + return + } + + ctx := context.Background() + + // Optionally set a transit ID on the context. This is useful to link your + // own request IDs to Paddle API requests. + ctx = paddle.ContextWithTransitID(ctx, "sdk-testing-request-1") + + simulation, err := client.UpdateSimulation( + ctx, + paddle.NewUpdateSimulationRequestSimulationSingleEventUpdate( + "ntfsim_01j9y0jwekrcyezscgkehvdmd6", + &paddle.SimulationSingleEventUpdate{ + NotificationSettingID: "ntfset_01j84xydheq48n3btebwf6ndn6", + Name: "Go SDK Test with Payload", + Type: "customer.created", + Payload: paddlenotification.CustomerNotification{ + ID: "ctm_01j870snka0xdp6szgyxze6d6d", + Name: paddle.PtrTo("John Doe"), + Email: "jane.doe@paddle.com", + MarketingConsent: false, + Status: paddlenotification.StatusActive, + CustomData: nil, + Locale: "en", + CreatedAt: time.Date(2024, 4, 12, 0, 0, 0, 0, time.UTC).Format(time.RFC3339), + UpdatedAt: time.Date(2024, 4, 12, 0, 0, 0, 0, time.UTC).Format(time.RFC3339), + ImportMeta: nil, + }, + }, + ), + ) + if err != nil { + return + } + + // Use type assertion on the response for payload + payload, ok := (simulation.Payload).(*paddlenotification.CustomerNotification) + if !ok { + fmt.Println("Payload is unexpected type") + return + } + + fmt.Println(simulation.ID) + fmt.Println(payload.ID) + // Output: + //ntfsim_01j9y0jwekrcyezscgkehvdmd6 + //ctm_01j870snka0xdp6szgyxze6d6d +} + +// // Demonstrates how to list Simulations with Payload and read the Payload back out of the response +func Example_simulation_list() { + // Create a mock HTTP server for this example - skip over this bit! + s := mockServerForExample(mockServerResponse{stub: &stub{paths: []stubPath{simulations}}}) + + // Create a new Paddle client. + client, err := paddle.New( + os.Getenv("PADDLE_API_KEY"), + paddle.WithBaseURL(s.URL), // Uses the mock server, you will not need this in your integration. + ) + + if err != nil { + fmt.Println(err) + return + } + + ctx := context.Background() + + // Optionally set a transit ID on the context. This is useful to link your + // own request IDs to Paddle API requests. + ctx = paddle.ContextWithTransitID(ctx, "sdk-testing-request-1") + + simulations, err := client.ListSimulations(ctx, &paddle.ListSimulationsRequest{}) + if err != nil { + } + + simulations.Iter(ctx, func(s *paddle.Simulation) (bool, error) { + switch p := s.Payload.(type) { + case *paddlenotification.AddressNotification: + // here v could be used as concrete type AddressNotification + fmt.Println(p.CustomerID) + case *paddlenotification.CustomerNotification: + // here v could be used as concrete type CustomerNotification + fmt.Println(p.Email) + } + + return true, nil + }) + // Output: + //john.doe+blackhole@paddle.com + //ctm_01hv6y1jedq4p1n0yqn5ba3ky4 +} + +// Demonstrates how to get a Simulation with Payload and read the Payload back out of the response +func Example_simulation_get() { + // Create a mock HTTP server for this example - skip over this bit! + s := mockServerForExample(mockServerResponse{stub: &stub{paths: []stubPath{simulation}}}) + + // Create a new Paddle client. + client, err := paddle.New( + os.Getenv("PADDLE_API_KEY"), + paddle.WithBaseURL(s.URL), // Uses the mock server, you will not need this in your integration. + ) + + if err != nil { + fmt.Println(err) + return + } + + ctx := context.Background() + + // Optionally set a transit ID on the context. This is useful to link your + // own request IDs to Paddle API requests. + ctx = paddle.ContextWithTransitID(ctx, "sdk-testing-request-1") + + simulation, err := client.GetSimulation(ctx, &paddle.GetSimulationRequest{SimulationID: "ntfsim_01j9y0jwekrcyezscgkehvdmd6"}) + if err != nil { + return + } + + // Use type assertion on the response for payload + payload, ok := (simulation.Payload).(*paddlenotification.CustomerNotification) + if !ok { + fmt.Println("Payload is unexpected type") + return + } + + fmt.Println(simulation.ID) + fmt.Println(payload.ID) + // Output: + //ntfsim_01j9y0jwekrcyezscgkehvdmd6 + //ctm_01j870snka0xdp6szgyxze6d6d +} + +// Demonstrates how to run a Simulation +func Example_simulation_run() { + // Create a mock HTTP server for this example - skip over this bit! + s := mockServerForExample(mockServerResponse{stub: &stub{paths: []stubPath{simulationRun}}}) + + // Create a new Paddle client. + client, err := paddle.New( + os.Getenv("PADDLE_API_KEY"), + paddle.WithBaseURL(s.URL), // Uses the mock server, you will not need this in your integration. + ) + + if err != nil { + fmt.Println(err) + return + } + + ctx := context.Background() + + // Optionally set a transit ID on the context. This is useful to link your + // own request IDs to Paddle API requests. + ctx = paddle.ContextWithTransitID(ctx, "sdk-testing-request-1") + + simulationRun, err := client.CreateSimulationRun(ctx, &paddle.CreateSimulationRunRequest{ + SimulationID: "ntfsim_01j9y0jwekrcyezscgkehvdmd6", + }) + if err != nil { + return + } + + fmt.Println(simulationRun.ID) + // Output: + //ntfsimrun_01j9yq3yspewy5r8zr05vkeekd +} + +// Demonstrates how to get a SimulationRun with included SimulationRunEvents +func Example_simulation_run_get() { + // Create a mock HTTP server for this example - skip over this bit! + s := mockServerForExample(mockServerResponse{stub: &stub{paths: []stubPath{simulationRunWithEvents}}}) + + // Create a new Paddle client. + client, err := paddle.New( + os.Getenv("PADDLE_API_KEY"), + paddle.WithBaseURL(s.URL), // Uses the mock server, you will not need this in your integration. + ) + + if err != nil { + fmt.Println(err) + return + } + + ctx := context.Background() + + // Optionally set a transit ID on the context. This is useful to link your + // own request IDs to Paddle API requests. + ctx = paddle.ContextWithTransitID(ctx, "sdk-testing-request-1") + + simulationRun, err := client.GetSimulationRun(ctx, &paddle.GetSimulationRunRequest{ + SimulationID: "ntfsim_01j9y0jwekrcyezscgkehvdmd6", + SimulationRunID: "ntfsimrun_01j9yq3yspewy5r8zr05vkeekd", + IncludeEvents: true, + }) + if err != nil { + return + } + + // Use type assertion on the response for payload + for _, event := range simulationRun.Events { + payload, ok := (event.Payload).(*paddlenotification.CustomerNotification) + if !ok { + fmt.Println("Payload is unexpected type") + return + } + fmt.Println(payload.ID) + fmt.Println(event.Response.StatusCode) + } + + fmt.Println(simulationRun.ID) + // Output: + //ctm_01j870snka0xdp6szgyxze6d6d + //200 + //ntfsimrun_01j9yq3yspewy5r8zr05vkeekd +} diff --git a/example_test.go b/example_test.go index 8858d6e..3a139b1 100644 --- a/example_test.go +++ b/example_test.go @@ -16,6 +16,10 @@ const ( transactionsPaginatedPg1 stubPath = "testdata/transactions_paginated_pg1.json" transactionsPaginatedPg2 stubPath = "testdata/transactions_paginated_pg2.json" priceCreatedEvent stubPath = "testdata/price_created.json" + simulation stubPath = "testdata/simulation.json" + simulations stubPath = "testdata/simulations.json" + simulationRun stubPath = "testdata/simulation_run.json" + simulationRunWithEvents stubPath = "testdata/simulation_run_with_events.json" ) //go:embed testdata diff --git a/internal/response/response.go b/internal/response/response.go index 57f433c..fff9830 100644 --- a/internal/response/response.go +++ b/internal/response/response.go @@ -13,6 +13,11 @@ import ( "github.com/PaddleHQ/paddle-go-sdk/pkg/paddleerr" ) +// UnmarshalsResponses is an interface implemented by type that must unmarshal responses for themselves. +type UnmarshalsResponses interface { + UnmarshalsResponses() +} + // Response is the wrapper response type returned by the Paddle API. type Response[T any] struct { Data T `json:"data"` @@ -58,7 +63,7 @@ func Handle(req *http.Request, res *http.Response, dst any) (err error) { teedBytes := bytes.NewBuffer([]byte{}) tee := io.TeeReader(res.Body, teedBytes) - if dst != nil && reflect.TypeOf(dst).Elem().Implements(reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()) { + if dst != nil && reflect.TypeOf(dst).Elem().Implements(reflect.TypeOf((*UnmarshalsResponses)(nil)).Elem()) { err = json.NewDecoder(tee).Decode(dst) } else { err = json.NewDecoder(tee).Decode(r) diff --git a/pkg/paddlenotification/addresses.go b/pkg/paddlenotification/addresses.go index 4f505db..0c3f63e 100644 --- a/pkg/paddlenotification/addresses.go +++ b/pkg/paddlenotification/addresses.go @@ -25,32 +25,34 @@ type AddressUpdated struct { // AddressNotification: New or changed entity. type AddressNotification struct { + NotificationPayload `json:"-"` + // ID: Unique Paddle ID for this address entity, prefixed with `add_`. - ID string `json:"id,omitempty"` + ID string `json:"id"` // CustomerID: Paddle ID for the customer related to this address, prefixed with `cus_`. - CustomerID string `json:"customer_id,omitempty"` + CustomerID string `json:"customer_id"` // Description: Memorable description for this address. - Description *string `json:"description,omitempty"` + Description *string `json:"description"` // FirstLine: First line of this address. - FirstLine *string `json:"first_line,omitempty"` + FirstLine *string `json:"first_line"` // SecondLine: Second line of this address. - SecondLine *string `json:"second_line,omitempty"` + SecondLine *string `json:"second_line"` // City: City of this address. - City *string `json:"city,omitempty"` + City *string `json:"city"` // PostalCode: ZIP or postal code of this address. Required for some countries. - PostalCode *string `json:"postal_code,omitempty"` + PostalCode *string `json:"postal_code"` // Region: State, county, or region of this address. - Region *string `json:"region,omitempty"` + Region *string `json:"region"` // CountryCode: Supported two-letter ISO 3166-1 alpha-2 country code for this address. - CountryCode CountryCode `json:"country_code,omitempty"` + CountryCode CountryCode `json:"country_code"` // CustomData: Your own structured key-value data. - CustomData CustomData `json:"custom_data,omitempty"` + CustomData CustomData `json:"custom_data"` // Status: Whether this entity can be used in Paddle. - Status Status `json:"status,omitempty"` + Status Status `json:"status"` // CreatedAt: RFC 3339 datetime string of when this entity was created. Set automatically by Paddle. - CreatedAt string `json:"created_at,omitempty"` + CreatedAt string `json:"created_at"` // UpdatedAt: RFC 3339 datetime string of when this entity was updated. Set automatically by Paddle. - UpdatedAt string `json:"updated_at,omitempty"` + UpdatedAt string `json:"updated_at"` // ImportMeta: Import information for this entity. `null` if this entity is not imported. - ImportMeta *ImportMeta `json:"import_meta,omitempty"` + ImportMeta *ImportMeta `json:"import_meta"` } diff --git a/pkg/paddlenotification/adjustments.go b/pkg/paddlenotification/adjustments.go index 4057b04..663c025 100644 --- a/pkg/paddlenotification/adjustments.go +++ b/pkg/paddlenotification/adjustments.go @@ -145,28 +145,30 @@ type AdjustmentTaxRateUsed struct { // AdjustmentNotification: New or changed entity. type AdjustmentNotification struct { + NotificationPayload `json:"-"` + // ID: Unique Paddle ID for this adjustment entity, prefixed with `adj_`. - ID string `json:"id,omitempty"` + ID string `json:"id"` // Action: How this adjustment impacts the related transaction. - Action AdjustmentAction `json:"action,omitempty"` + Action AdjustmentAction `json:"action"` // TransactionID: Paddle ID of the transaction that this adjustment is for, prefixed with `txn_`. - TransactionID string `json:"transaction_id,omitempty"` + TransactionID string `json:"transaction_id"` /* SubscriptionID: Paddle ID for the subscription related to this adjustment, prefixed with `sub_`. Set automatically by Paddle based on the `subscription_id` of the related transaction. */ - SubscriptionID *string `json:"subscription_id,omitempty"` + SubscriptionID *string `json:"subscription_id"` /* CustomerID: Paddle ID for the customer related to this adjustment, prefixed with `ctm_`. Set automatically by Paddle based on the `customer_id` of the related transaction. */ - CustomerID string `json:"customer_id,omitempty"` + CustomerID string `json:"customer_id"` // Reason: Why this adjustment was created. Appears in the Paddle dashboard. Retained for record-keeping purposes. - Reason string `json:"reason,omitempty"` + Reason string `json:"reason"` // CreditAppliedToBalance: Whether this adjustment was applied to the related customer's credit balance. `null` unless adjustment `action` is not `credit`. - CreditAppliedToBalance *bool `json:"credit_applied_to_balance,omitempty"` + CreditAppliedToBalance *bool `json:"credit_applied_to_balance"` // CurrencyCode: Three-letter ISO 4217 currency code for this adjustment. Set automatically by Paddle based on the `currency_code` of the related transaction. - CurrencyCode CurrencyCode `json:"currency_code,omitempty"` + CurrencyCode CurrencyCode `json:"currency_code"` /* Status: Status of this adjustment. Set automatically by Paddle. @@ -174,17 +176,17 @@ type AdjustmentNotification struct { Credit adjustments don't require approval from Paddle, so they're created as `approved`. */ - Status AdjustmentStatus `json:"status,omitempty"` + Status AdjustmentStatus `json:"status"` // Items: List of items on this adjustment. - Items []AdjustmentItem `json:"items,omitempty"` + Items []AdjustmentItem `json:"items"` // Totals: Breakdown of the total for an adjustment. - Totals AdjustmentTotals `json:"totals,omitempty"` + Totals AdjustmentTotals `json:"totals"` // PayoutTotals: Breakdown of how this adjustment affects your payout balance. - PayoutTotals *PayoutTotalsAdjustment `json:"payout_totals,omitempty"` + PayoutTotals *PayoutTotalsAdjustment `json:"payout_totals"` // TaxRatesUsed: List of tax rates applied for this adjustment. - TaxRatesUsed []AdjustmentTaxRateUsed `json:"tax_rates_used,omitempty"` + TaxRatesUsed []AdjustmentTaxRateUsed `json:"tax_rates_used"` // CreatedAt: RFC 3339 datetime string of when this entity was created. Set automatically by Paddle. - CreatedAt string `json:"created_at,omitempty"` + CreatedAt string `json:"created_at"` // UpdatedAt: RFC 3339 datetime string of when this entity was updated. Set automatically by Paddle. - UpdatedAt string `json:"updated_at,omitempty"` + UpdatedAt string `json:"updated_at"` } diff --git a/pkg/paddlenotification/businesses.go b/pkg/paddlenotification/businesses.go index 0934c97..99338ce 100644 --- a/pkg/paddlenotification/businesses.go +++ b/pkg/paddlenotification/businesses.go @@ -33,26 +33,28 @@ type Contacts struct { // BusinessNotification: New or changed entity. type BusinessNotification struct { + NotificationPayload `json:"-"` + // ID: Unique Paddle ID for this business entity, prefixed with `biz_`. - ID string `json:"id,omitempty"` + ID string `json:"id"` // CustomerID: Paddle ID for the customer related to this business, prefixed with `cus_`. - CustomerID string `json:"customer_id,omitempty"` + CustomerID string `json:"customer_id"` // Name: Name of this business. - Name string `json:"name,omitempty"` + Name string `json:"name"` // CompanyNumber: Company number for this business. - CompanyNumber *string `json:"company_number,omitempty"` + CompanyNumber *string `json:"company_number"` // TaxIdentifier: Tax or VAT Number for this business. - TaxIdentifier *string `json:"tax_identifier,omitempty"` + TaxIdentifier *string `json:"tax_identifier"` // Status: Whether this entity can be used in Paddle. - Status Status `json:"status,omitempty"` + Status Status `json:"status"` // Contacts: List of contacts related to this business, typically used for sending invoices. - Contacts []Contacts `json:"contacts,omitempty"` + Contacts []Contacts `json:"contacts"` // CreatedAt: RFC 3339 datetime string of when this entity was created. Set automatically by Paddle. - CreatedAt string `json:"created_at,omitempty"` + CreatedAt string `json:"created_at"` // UpdatedAt: RFC 3339 datetime string of when this entity was updated. Set automatically by Paddle. - UpdatedAt string `json:"updated_at,omitempty"` + UpdatedAt string `json:"updated_at"` // CustomData: Your own structured key-value data. - CustomData CustomData `json:"custom_data,omitempty"` + CustomData CustomData `json:"custom_data"` // ImportMeta: Import information for this entity. `null` if this entity is not imported. - ImportMeta *ImportMeta `json:"import_meta,omitempty"` + ImportMeta *ImportMeta `json:"import_meta"` } diff --git a/pkg/paddlenotification/customers.go b/pkg/paddlenotification/customers.go index 7999f30..8e60b36 100644 --- a/pkg/paddlenotification/customers.go +++ b/pkg/paddlenotification/customers.go @@ -25,27 +25,29 @@ type CustomerUpdated struct { // CustomerNotification: New or changed entity. type CustomerNotification struct { + NotificationPayload `json:"-"` + // ID: Unique Paddle ID for this customer entity, prefixed with `ctm_`. - ID string `json:"id,omitempty"` + ID string `json:"id"` // Name: Full name of this customer. Required when creating transactions where `collection_mode` is `manual` (invoices). - Name *string `json:"name,omitempty"` + Name *string `json:"name"` // Email: Email address for this customer. - Email string `json:"email,omitempty"` + Email string `json:"email"` /* MarketingConsent: Whether this customer opted into marketing from you. `false` unless customers check the marketing consent box when using Paddle Checkout. Set automatically by Paddle. */ - MarketingConsent bool `json:"marketing_consent,omitempty"` + MarketingConsent bool `json:"marketing_consent"` // Status: Whether this entity can be used in Paddle. - Status Status `json:"status,omitempty"` + Status Status `json:"status"` // CustomData: Your own structured key-value data. - CustomData CustomData `json:"custom_data,omitempty"` + CustomData CustomData `json:"custom_data"` // Locale: Valid IETF BCP 47 short form locale tag. - Locale string `json:"locale,omitempty"` + Locale string `json:"locale"` // CreatedAt: RFC 3339 datetime string of when this entity was created. Set automatically by Paddle. - CreatedAt string `json:"created_at,omitempty"` + CreatedAt string `json:"created_at"` // UpdatedAt: RFC 3339 datetime string of when this entity was updated. Set automatically by Paddle. - UpdatedAt string `json:"updated_at,omitempty"` + UpdatedAt string `json:"updated_at"` // ImportMeta: Import information for this entity. `null` if this entity is not imported. - ImportMeta *ImportMeta `json:"import_meta,omitempty"` + ImportMeta *ImportMeta `json:"import_meta"` } diff --git a/pkg/paddlenotification/discounts.go b/pkg/paddlenotification/discounts.go index 1a86a4c..7c82497 100644 --- a/pkg/paddlenotification/discounts.go +++ b/pkg/paddlenotification/discounts.go @@ -44,50 +44,52 @@ const ( // DiscountNotification: New or changed entity. type DiscountNotification struct { + NotificationPayload `json:"-"` + // ID: Unique Paddle ID for this discount, prefixed with `dsc_`. - ID string `json:"id,omitempty"` + ID string `json:"id"` // Status: Whether this entity can be used in Paddle. `expired` and `used` are set automatically by Paddle. - Status DiscountStatus `json:"status,omitempty"` + Status DiscountStatus `json:"status"` // Description: Short description for this discount for your reference. Not shown to customers. - Description string `json:"description,omitempty"` + Description string `json:"description"` // EnabledForCheckout: Whether this discount can be redeemed by customers at checkout (`true`) or not (`false`). - EnabledForCheckout bool `json:"enabled_for_checkout,omitempty"` + EnabledForCheckout bool `json:"enabled_for_checkout"` // Code: Unique code that customers can use to redeem this discount at checkout. - Code *string `json:"code,omitempty"` + Code *string `json:"code"` // Type: Type of discount. Determines how this discount impacts the checkout or transaction total. - Type Type `json:"type,omitempty"` + Type Type `json:"type"` // Amount: Amount to discount by. For `percentage` discounts, must be an amount between `0.01` and `100`. For `flat` and `flat_per_seat` discounts, amount in the lowest denomination for a currency. - Amount string `json:"amount,omitempty"` + Amount string `json:"amount"` // CurrencyCode: Supported three-letter ISO 4217 currency code. Required where discount type is `flat` or `flat_per_seat`. - CurrencyCode *CurrencyCode `json:"currency_code,omitempty"` + CurrencyCode *CurrencyCode `json:"currency_code"` // Recur: Whether this discount applies for multiple subscription billing periods (`true`) or not (`false`). - Recur bool `json:"recur,omitempty"` + Recur bool `json:"recur"` /* MaximumRecurringIntervals: Number of subscription billing periods that this discount recurs for. Requires `recur`. `null` if this discount recurs forever. Subscription renewals, midcycle changes, and one-time charges billed to a subscription aren't considered a redemption. `times_used` is not incremented in these cases. */ - MaximumRecurringIntervals *int `json:"maximum_recurring_intervals,omitempty"` + MaximumRecurringIntervals *int `json:"maximum_recurring_intervals"` /* UsageLimit: Maximum number of times this discount can be redeemed. This is an overall limit for this discount, rather than a per-customer limit. `null` if this discount can be redeemed an unlimited amount of times. Paddle counts a usage as a redemption on a checkout, transaction, or the initial application against a subscription. Transactions created for subscription renewals, midcycle changes, and one-time charges aren't considered a redemption. */ - UsageLimit *int `json:"usage_limit,omitempty"` + UsageLimit *int `json:"usage_limit"` // RestrictTo: Product or price IDs that this discount is for. When including a product ID, all prices for that product can be discounted. `null` if this discount applies to all products and prices. - RestrictTo []string `json:"restrict_to,omitempty"` + RestrictTo []string `json:"restrict_to"` // CustomData: Your own structured key-value data. - CustomData CustomData `json:"custom_data,omitempty"` + CustomData CustomData `json:"custom_data"` // ImportMeta: Import information for this entity. `null` if this entity is not imported. - ImportMeta *ImportMeta `json:"import_meta,omitempty"` + ImportMeta *ImportMeta `json:"import_meta"` /* ExpiresAt: RFC 3339 datetime string of when this discount expires. Discount can no longer be redeemed after this date has elapsed. `null` if this discount can be redeemed forever. Expired discounts can't be redeemed against transactions or checkouts, but can be applied when updating subscriptions. */ - ExpiresAt *string `json:"expires_at,omitempty"` + ExpiresAt *string `json:"expires_at"` // CreatedAt: RFC 3339 datetime string of when this entity was created. Set automatically by Paddle. - CreatedAt string `json:"created_at,omitempty"` + CreatedAt string `json:"created_at"` // UpdatedAt: RFC 3339 datetime string of when this entity was updated. Set automatically by Paddle. - UpdatedAt string `json:"updated_at,omitempty"` + UpdatedAt string `json:"updated_at"` } diff --git a/pkg/paddlenotification/notification.go b/pkg/paddlenotification/notification.go index 852b979..b8ee672 100644 --- a/pkg/paddlenotification/notification.go +++ b/pkg/paddlenotification/notification.go @@ -4,6 +4,8 @@ package paddlenotification type NotificationEvent interface{} +type NotificationPayload interface{} + // GenericNotificationEvent: Notification payload. Includes the new or changed event. type GenericNotificationEvent struct { NotificationEvent @@ -15,7 +17,7 @@ type GenericNotificationEvent struct { // OccurredAt: RFC 3339 datetime string of when this event occurred. OccurredAt string `json:"occurred_at,omitempty"` // Data: New or changed entity. - Data any `json:"data,omitempty"` + Data NotificationPayload `json:"data,omitempty"` // NotificationID: Unique Paddle ID for this notification, prefixed with `ntf_`. NotificationID string `json:"notification_id,omitempty"` } diff --git a/pkg/paddlenotification/payouts.go b/pkg/paddlenotification/payouts.go index 85c5b13..5f95b23 100644 --- a/pkg/paddlenotification/payouts.go +++ b/pkg/paddlenotification/payouts.go @@ -18,12 +18,14 @@ type PayoutPaid struct { // PayoutNotification: New or changed entity. type PayoutNotification struct { + NotificationPayload `json:"-"` + // ID: ID for this payout. - ID string `json:"id,omitempty"` + ID string `json:"id"` // Status: Status of this payout. - Status Status `json:"status,omitempty"` + Status Status `json:"status"` // Amount: Amount paid, or scheduled to be paid, for this payout. - Amount string `json:"amount,omitempty"` + Amount string `json:"amount"` // CurrencyCode: Three-letter ISO 4217 currency code for this payout. - CurrencyCode CurrencyCodePayouts `json:"currency_code,omitempty"` + CurrencyCode CurrencyCodePayouts `json:"currency_code"` } diff --git a/pkg/paddlenotification/prices.go b/pkg/paddlenotification/prices.go index 4c72fd8..64aa888 100644 --- a/pkg/paddlenotification/prices.go +++ b/pkg/paddlenotification/prices.go @@ -25,36 +25,38 @@ type PriceUpdated struct { // PriceNotification: Represents a price entity. type PriceNotification struct { + NotificationPayload `json:"-"` + // ID: Unique Paddle ID for this price, prefixed with `pri_`. - ID string `json:"id,omitempty"` + ID string `json:"id"` // ProductID: Paddle ID for the product that this price is for, prefixed with `pro_`. - ProductID string `json:"product_id,omitempty"` + ProductID string `json:"product_id"` // Description: Internal description for this price, not shown to customers. Typically notes for your team. - Description string `json:"description,omitempty"` + Description string `json:"description"` // Type: Type of item. Standard items are considered part of your catalog and are shown on the Paddle web app. - Type *CatalogType `json:"type,omitempty"` + Type *CatalogType `json:"type"` // Name: Name of this price, shown to customers at checkout and on invoices. Typically describes how often the related product bills. - Name *string `json:"name,omitempty"` + Name *string `json:"name"` // BillingCycle: How often this price should be charged. `null` if price is non-recurring (one-time). - BillingCycle *Duration `json:"billing_cycle,omitempty"` + BillingCycle *Duration `json:"billing_cycle"` // TrialPeriod: Trial period for the product related to this price. The billing cycle begins once the trial period is over. `null` for no trial period. Requires `billing_cycle`. - TrialPeriod *Duration `json:"trial_period,omitempty"` + TrialPeriod *Duration `json:"trial_period"` // TaxMode: How tax is calculated for this price. - TaxMode TaxMode `json:"tax_mode,omitempty"` + TaxMode TaxMode `json:"tax_mode"` // UnitPrice: Base price. This price applies to all customers, except for customers located in countries where you have `unit_price_overrides`. - UnitPrice Money `json:"unit_price,omitempty"` + UnitPrice Money `json:"unit_price"` // UnitPriceOverrides: List of unit price overrides. Use to override the base price with a custom price and currency for a country or group of countries. - UnitPriceOverrides []UnitPriceOverride `json:"unit_price_overrides,omitempty"` + UnitPriceOverrides []UnitPriceOverride `json:"unit_price_overrides"` // Quantity: Limits on how many times the related product can be purchased at this price. Useful for discount campaigns. - Quantity PriceQuantity `json:"quantity,omitempty"` + Quantity PriceQuantity `json:"quantity"` // Status: Whether this entity can be used in Paddle. - Status Status `json:"status,omitempty"` + Status Status `json:"status"` // CustomData: Your own structured key-value data. - CustomData CustomData `json:"custom_data,omitempty"` + CustomData CustomData `json:"custom_data"` // ImportMeta: Import information for this entity. `null` if this entity is not imported. - ImportMeta *ImportMeta `json:"import_meta,omitempty"` + ImportMeta *ImportMeta `json:"import_meta"` // CreatedAt: RFC 3339 datetime string of when this entity was created. Set automatically by Paddle. - CreatedAt *string `json:"created_at,omitempty"` + CreatedAt *string `json:"created_at"` // UpdatedAt: RFC 3339 datetime string of when this entity was updated. Set automatically by Paddle. - UpdatedAt *string `json:"updated_at,omitempty"` + UpdatedAt *string `json:"updated_at"` } diff --git a/pkg/paddlenotification/products.go b/pkg/paddlenotification/products.go index 25c32c5..88bfc72 100644 --- a/pkg/paddlenotification/products.go +++ b/pkg/paddlenotification/products.go @@ -25,26 +25,28 @@ type ProductUpdated struct { // ProductNotification: Represents a product entity. type ProductNotification struct { + NotificationPayload `json:"-"` + // ID: Unique Paddle ID for this product, prefixed with `pro_`. - ID string `json:"id,omitempty"` + ID string `json:"id"` // Name: Name of this product. - Name string `json:"name,omitempty"` + Name string `json:"name"` // Description: Short description for this product. - Description *string `json:"description,omitempty"` + Description *string `json:"description"` // Type: Type of item. Standard items are considered part of your catalog and are shown on the Paddle web app. - Type *CatalogType `json:"type,omitempty"` + Type *CatalogType `json:"type"` // TaxCategory: Tax category for this product. Used for charging the correct rate of tax. Selected tax category must be enabled on your Paddle account. - TaxCategory TaxCategory `json:"tax_category,omitempty"` + TaxCategory TaxCategory `json:"tax_category"` // ImageURL: Image for this product. Included in the checkout and on some customer documents. - ImageURL *string `json:"image_url,omitempty"` + ImageURL *string `json:"image_url"` // CustomData: Your own structured key-value data. - CustomData CustomData `json:"custom_data,omitempty"` + CustomData CustomData `json:"custom_data"` // Status: Whether this entity can be used in Paddle. - Status Status `json:"status,omitempty"` + Status Status `json:"status"` // ImportMeta: Import information for this entity. `null` if this entity is not imported. - ImportMeta *ImportMeta `json:"import_meta,omitempty"` + ImportMeta *ImportMeta `json:"import_meta"` // CreatedAt: RFC 3339 datetime string of when this entity was created. Set automatically by Paddle. - CreatedAt string `json:"created_at,omitempty"` + CreatedAt string `json:"created_at"` // UpdatedAt: RFC 3339 datetime string of when this entity was updated. Set automatically by Paddle. - UpdatedAt *string `json:"updated_at,omitempty"` + UpdatedAt *string `json:"updated_at"` } diff --git a/pkg/paddlenotification/reports.go b/pkg/paddlenotification/reports.go index 4a794a1..744f17c 100644 --- a/pkg/paddlenotification/reports.go +++ b/pkg/paddlenotification/reports.go @@ -81,24 +81,26 @@ type ReportFilters struct { // ReportNotification: New or changed entity. type ReportNotification struct { + NotificationPayload `json:"-"` + // ID: Unique Paddle ID for this report, prefixed with `rep_` - ID string `json:"id,omitempty"` + ID string `json:"id"` /* Status: Status of this report. Set automatically by Paddle. Reports are created as `pending` initially, then move to `ready` when they're available to download. */ - Status ReportStatus `json:"status,omitempty"` + Status ReportStatus `json:"status"` // Rows: Number of records in this report. `null` if the report is `pending`. - Rows *int `json:"rows,omitempty"` + Rows *int `json:"rows"` // ExpiresAt: RFC 3339 datetime string of when this report expires. The report is no longer available to download after this date. - ExpiresAt *string `json:"expires_at,omitempty"` + ExpiresAt *string `json:"expires_at"` // UpdatedAt: RFC 3339 datetime string of when this report was last updated. - UpdatedAt string `json:"updated_at,omitempty"` + UpdatedAt string `json:"updated_at"` // CreatedAt: RFC 3339 datetime string of when this report was created. - CreatedAt string `json:"created_at,omitempty"` + CreatedAt string `json:"created_at"` // Type: Type of report to create. - Type ReportType `json:"type,omitempty"` + Type ReportType `json:"type"` // Filters: Filter criteria for this report. If omitted when creating, reports are filtered to include data updated in the last 30 days. This means `updated_at` is greater than or equal to (`gte`) the date 30 days ago from the time the report was generated. - Filters []ReportFilters `json:"filters,omitempty"` + Filters []ReportFilters `json:"filters"` } diff --git a/pkg/paddlenotification/subscriptions.go b/pkg/paddlenotification/subscriptions.go index 2de698e..5371938 100644 --- a/pkg/paddlenotification/subscriptions.go +++ b/pkg/paddlenotification/subscriptions.go @@ -140,54 +140,58 @@ type SubscriptionItem struct { // SubscriptionNotification: New or changed entity. type SubscriptionNotification struct { + NotificationPayload `json:"-"` + // ID: Unique Paddle ID for this subscription entity, prefixed with `sub_`. - ID string `json:"id,omitempty"` + ID string `json:"id"` // Status: Status of this subscription. Set automatically by Paddle. Use the pause subscription or cancel subscription operations to change. - Status SubscriptionStatus `json:"status,omitempty"` + Status SubscriptionStatus `json:"status"` // CustomerID: Paddle ID of the customer that this subscription is for, prefixed with `ctm_`. - CustomerID string `json:"customer_id,omitempty"` + CustomerID string `json:"customer_id"` // AddressID: Paddle ID of the address that this subscription is for, prefixed with `add_`. - AddressID string `json:"address_id,omitempty"` + AddressID string `json:"address_id"` // BusinessID: Paddle ID of the business that this subscription is for, prefixed with `biz_`. - BusinessID *string `json:"business_id,omitempty"` + BusinessID *string `json:"business_id"` // CurrencyCode: Supported three-letter ISO 4217 currency code. Transactions for this subscription are created in this currency. Must be `USD`, `EUR`, or `GBP` if `collection_mode` is `manual`. - CurrencyCode CurrencyCode `json:"currency_code,omitempty"` + CurrencyCode CurrencyCode `json:"currency_code"` // CreatedAt: RFC 3339 datetime string of when this entity was created. Set automatically by Paddle. - CreatedAt string `json:"created_at,omitempty"` + CreatedAt string `json:"created_at"` // UpdatedAt: RFC 3339 datetime string of when this entity was updated. Set automatically by Paddle. - UpdatedAt string `json:"updated_at,omitempty"` + UpdatedAt string `json:"updated_at"` // StartedAt: RFC 3339 datetime string of when this subscription started. This may be different from `first_billed_at` if the subscription started in trial. - StartedAt *string `json:"started_at,omitempty"` + StartedAt *string `json:"started_at"` // FirstBilledAt: RFC 3339 datetime string of when this subscription was first billed. This may be different from `started_at` if the subscription started in trial. - FirstBilledAt *string `json:"first_billed_at,omitempty"` + FirstBilledAt *string `json:"first_billed_at"` // NextBilledAt: RFC 3339 datetime string of when this subscription is next scheduled to be billed. - NextBilledAt *string `json:"next_billed_at,omitempty"` + NextBilledAt *string `json:"next_billed_at"` // PausedAt: RFC 3339 datetime string of when this subscription was paused. Set automatically by Paddle when the pause subscription operation is used. `null` if not paused. - PausedAt *string `json:"paused_at,omitempty"` + PausedAt *string `json:"paused_at"` // CanceledAt: RFC 3339 datetime string of when this subscription was canceled. Set automatically by Paddle when the cancel subscription operation is used. `null` if not canceled. - CanceledAt *string `json:"canceled_at,omitempty"` + CanceledAt *string `json:"canceled_at"` // Discount: Details of the discount applied to this subscription. - Discount *SubscriptionDiscountTimePeriod `json:"discount,omitempty"` + Discount *SubscriptionDiscountTimePeriod `json:"discount"` // CollectionMode: How payment is collected for transactions created for this subscription. `automatic` for checkout, `manual` for invoices. - CollectionMode CollectionMode `json:"collection_mode,omitempty"` + CollectionMode CollectionMode `json:"collection_mode"` // BillingDetails: Details for invoicing. Required if `collection_mode` is `manual`. - BillingDetails *BillingDetails `json:"billing_details,omitempty"` + BillingDetails *BillingDetails `json:"billing_details"` // CurrentBillingPeriod: Current billing period for this subscription. Set automatically by Paddle based on the billing cycle. `null` for `paused` and `canceled` subscriptions. - CurrentBillingPeriod *TimePeriod `json:"current_billing_period,omitempty"` + CurrentBillingPeriod *TimePeriod `json:"current_billing_period"` // BillingCycle: How often this subscription renews. Set automatically by Paddle based on the prices on this subscription. - BillingCycle Duration `json:"billing_cycle,omitempty"` + BillingCycle Duration `json:"billing_cycle"` // ScheduledChange: Change that's scheduled to be applied to a subscription. Use the pause subscription, cancel subscription, and resume subscription operations to create scheduled changes. `null` if no scheduled changes. - ScheduledChange *SubscriptionScheduledChange `json:"scheduled_change,omitempty"` + ScheduledChange *SubscriptionScheduledChange `json:"scheduled_change"` // Items: Represents a subscription item. - Items []SubscriptionItem `json:"items,omitempty"` + Items []SubscriptionItem `json:"items"` // CustomData: Your own structured key-value data. - CustomData CustomData `json:"custom_data,omitempty"` + CustomData CustomData `json:"custom_data"` // ImportMeta: Import information for this entity. `null` if this entity is not imported. - ImportMeta *ImportMeta `json:"import_meta,omitempty"` + ImportMeta *ImportMeta `json:"import_meta"` } // SubscriptionCreatedNotification: New or changed entity. type SubscriptionCreatedNotification struct { + NotificationPayload `json:"-"` + // ID: Unique Paddle ID for this subscription entity, prefixed with `sub_`. ID string `json:"id,omitempty"` // TransactionID: Paddle ID for the transaction entity that resulted in this subscription being created, prefixed with `txn_`. diff --git a/pkg/paddlenotification/transactions.go b/pkg/paddlenotification/transactions.go index 885300f..8dbb2fb 100644 --- a/pkg/paddlenotification/transactions.go +++ b/pkg/paddlenotification/transactions.go @@ -384,48 +384,50 @@ type Checkout struct { // TransactionNotification: New or changed entity. type TransactionNotification struct { + NotificationPayload `json:"-"` + // ID: Unique Paddle ID for this transaction entity, prefixed with `txn_`. - ID string `json:"id,omitempty"` + ID string `json:"id"` // Status: Status of this transaction. You may set a transaction to `billed` or `canceled`, other statuses are set automatically by Paddle. Automatically-collected transactions may return `completed` if payment is captured successfully, or `past_due` if payment failed. - Status TransactionStatus `json:"status,omitempty"` + Status TransactionStatus `json:"status"` // CustomerID: Paddle ID of the customer that this transaction is for, prefixed with `ctm_`. - CustomerID *string `json:"customer_id,omitempty"` + CustomerID *string `json:"customer_id"` // AddressID: Paddle ID of the address that this transaction is for, prefixed with `add_`. - AddressID *string `json:"address_id,omitempty"` + AddressID *string `json:"address_id"` // BusinessID: Paddle ID of the business that this transaction is for, prefixed with `biz_`. - BusinessID *string `json:"business_id,omitempty"` + BusinessID *string `json:"business_id"` // CustomData: Your own structured key-value data. - CustomData CustomData `json:"custom_data,omitempty"` + CustomData CustomData `json:"custom_data"` // CurrencyCode: Supported three-letter ISO 4217 currency code. Must be `USD`, `EUR`, or `GBP` if `collection_mode` is `manual`. - CurrencyCode CurrencyCode `json:"currency_code,omitempty"` + CurrencyCode CurrencyCode `json:"currency_code"` // Origin: Describes how this transaction was created. - Origin TransactionOrigin `json:"origin,omitempty"` + Origin TransactionOrigin `json:"origin"` // SubscriptionID: Paddle ID of the subscription that this transaction is for, prefixed with `sub_`. - SubscriptionID *string `json:"subscription_id,omitempty"` + SubscriptionID *string `json:"subscription_id"` // InvoiceID: Paddle ID of the invoice that this transaction is related to, prefixed with `inv_`. Used for compatibility with the Paddle Invoice API, which is now deprecated. This field is scheduled to be removed in the next version of the Paddle API. - InvoiceID *string `json:"invoice_id,omitempty"` + InvoiceID *string `json:"invoice_id"` // InvoiceNumber: Invoice number for this transaction. Automatically generated by Paddle when you mark a transaction as `billed` where `collection_mode` is `manual`. - InvoiceNumber *string `json:"invoice_number,omitempty"` + InvoiceNumber *string `json:"invoice_number"` // CollectionMode: How payment is collected for this transaction. `automatic` for checkout, `manual` for invoices. - CollectionMode CollectionMode `json:"collection_mode,omitempty"` + CollectionMode CollectionMode `json:"collection_mode"` // DiscountID: Paddle ID of the discount applied to this transaction, prefixed with `dsc_`. - DiscountID *string `json:"discount_id,omitempty"` + DiscountID *string `json:"discount_id"` // BillingDetails: Details for invoicing. Required if `collection_mode` is `manual`. - BillingDetails *BillingDetails `json:"billing_details,omitempty"` + BillingDetails *BillingDetails `json:"billing_details"` // BillingPeriod: Time period that this transaction is for. Set automatically by Paddle for subscription renewals to describe the period that charges are for. - BillingPeriod *TimePeriod `json:"billing_period,omitempty"` + BillingPeriod *TimePeriod `json:"billing_period"` // Items: List of items on this transaction. For calculated totals, use `details.line_items`. - Items []TransactionItem `json:"items,omitempty"` + Items []TransactionItem `json:"items"` // Details: Calculated totals for a transaction, including proration, discounts, tax, and currency conversion. Considered the source of truth for totals on a transaction. - Details TransactionDetails `json:"details,omitempty"` + Details TransactionDetails `json:"details"` // Payments: List of payment attempts for this transaction, including successful payments. Sorted by `created_at` in descending order, so most recent attempts are returned first. - Payments []TransactionPaymentAttempt `json:"payments,omitempty"` + Payments []TransactionPaymentAttempt `json:"payments"` // Checkout: Paddle Checkout details for this transaction. Returned for automatically-collected transactions and where `billing_details.enable_checkout` is `true` for manually-collected transactions; `null` otherwise. - Checkout *Checkout `json:"checkout,omitempty"` + Checkout *Checkout `json:"checkout"` // CreatedAt: RFC 3339 datetime string of when this entity was created. Set automatically by Paddle. - CreatedAt string `json:"created_at,omitempty"` + CreatedAt string `json:"created_at"` // UpdatedAt: RFC 3339 datetime string of when this entity was updated. Set automatically by Paddle. - UpdatedAt string `json:"updated_at,omitempty"` + UpdatedAt string `json:"updated_at"` // BilledAt: RFC 3339 datetime string of when this transaction was marked as `billed`. `null` for transactions that are not `billed` or `completed`. Set automatically by Paddle. - BilledAt *string `json:"billed_at,omitempty"` + BilledAt *string `json:"billed_at"` } diff --git a/shared.go b/shared.go index 0af7b83..134b0b4 100644 --- a/shared.go +++ b/shared.go @@ -2,7 +2,13 @@ package paddle -import paddleerr "github.com/PaddleHQ/paddle-go-sdk/pkg/paddleerr" +import ( + "encoding/json" + "strings" + + paddleerr "github.com/PaddleHQ/paddle-go-sdk/pkg/paddleerr" + "github.com/PaddleHQ/paddle-go-sdk/pkg/paddlenotification" +) // ErrNotFound represents a `not_found` error. // See https://developer.paddle.com/errors/shared/not_found for more information. @@ -1485,9 +1491,6 @@ const ( SimulationTypeNameSubscriptionCancellation SimulationTypeName = "subscription_cancellation" ) -// Payload: Simulation payload. `null` for scenarios. -type Payload map[string]any - // SimulationEventStatus: Status of this simulation run log.. type SimulationEventStatus string @@ -1524,7 +1527,7 @@ type SimulationEvent struct { // EventType: Type of event sent by Paddle, in the format `entity.event_type`. EventType EventTypeName `json:"event_type,omitempty"` // Payload: Simulation payload. Pass a JSON object that matches the schema for an event type to simulate a custom payload. If omitted, Paddle populates with a demo example. - Payload Payload `json:"payload,omitempty"` + Payload paddlenotification.NotificationPayload `json:"payload,omitempty"` // Request: Information about the request. Sent by Paddle as part of the simulation. Request *SimulationEventRequest `json:"request,omitempty"` // Response: Information about the response. Sent by the responding server for the notification setting. @@ -1534,3 +1537,52 @@ type SimulationEvent struct { // UpdatedAt: RFC 3339 datetime string of when this entity was updated. Set automatically by Paddle. UpdatedAt string `json:"updated_at,omitempty"` } + +// UnmarshalJSON implements the json.Unmarshaler interface for Notification +func (n *SimulationEvent) UnmarshalJSON(data []byte) error { + type alias SimulationEvent + if err := json.Unmarshal(data, (*alias)(n)); err != nil { + return err + } + + var t paddlenotification.NotificationPayload + switch strings.Split(string(n.EventType), ".")[0] { + case "address": + t = &paddlenotification.AddressNotification{} + case "adjustment": + t = &paddlenotification.AdjustmentNotification{} + case "business": + t = &paddlenotification.BusinessNotification{} + case "customer": + t = &paddlenotification.CustomerNotification{} + case "discount": + t = &paddlenotification.DiscountNotification{} + case "payout": + t = &paddlenotification.PayoutNotification{} + case "price": + t = &paddlenotification.PriceNotification{} + case "product": + t = &paddlenotification.ProductNotification{} + case "report": + t = &paddlenotification.ReportNotification{} + case "subscription": + t = &paddlenotification.SubscriptionNotification{} + case "transaction": + t = &paddlenotification.TransactionNotification{} + default: + t = map[string]any{} + } + + rawT, err := json.Marshal(n.Payload) + if err != nil { + return err + } + + if err := json.Unmarshal(rawT, t); err != nil { + return err + } + + n.Payload = t + + return nil +} diff --git a/simulations.go b/simulations.go index fd1a74f..ede0d11 100644 --- a/simulations.go +++ b/simulations.go @@ -5,6 +5,9 @@ package paddle import ( "context" "encoding/json" + "strings" + + "github.com/PaddleHQ/paddle-go-sdk/pkg/paddlenotification" ) // Simulation: Represents a simulation entity. @@ -20,7 +23,7 @@ type Simulation struct { // Type: Single event sent for this simulation, in the format `entity.event_type`. Type SimulationTypeName `json:"type,omitempty"` // Payload: Simulation payload. `null` for scenarios. - Payload *Payload `json:"payload,omitempty"` + Payload paddlenotification.NotificationPayload `json:"payload,omitempty"` // LastRunAt: RFC 3339 datetime string of when this simulation was last run. `null` until run. Set automatically by Paddle. LastRunAt *string `json:"last_run_at,omitempty"` // CreatedAt: RFC 3339 datetime string of when this entity was created. Set automatically by Paddle. @@ -29,6 +32,60 @@ type Simulation struct { UpdatedAt string `json:"updated_at,omitempty"` } +// UnmarshalJSON implements the json.Unmarshaler interface for Notification +func (s *Simulation) UnmarshalJSON(data []byte) error { + type alias Simulation + + if err := json.Unmarshal(data, (*alias)(s)); err != nil { + return err + } + + if s.Payload == nil || !strings.Contains(string(s.Type), ".") { + return nil + } + + var t paddlenotification.NotificationPayload + switch strings.Split(string(s.Type), ".")[0] { + case "address": + t = &paddlenotification.AddressNotification{} + case "adjustment": + t = &paddlenotification.AdjustmentNotification{} + case "business": + t = &paddlenotification.BusinessNotification{} + case "customer": + t = &paddlenotification.CustomerNotification{} + case "discount": + t = &paddlenotification.DiscountNotification{} + case "payout": + t = &paddlenotification.PayoutNotification{} + case "price": + t = &paddlenotification.PriceNotification{} + case "product": + t = &paddlenotification.ProductNotification{} + case "report": + t = &paddlenotification.ReportNotification{} + case "subscription": + t = &paddlenotification.SubscriptionNotification{} + case "transaction": + t = &paddlenotification.TransactionNotification{} + default: + t = map[string]any{} + } + + rawT, err := json.Marshal(s.Payload) + if err != nil { + return err + } + + if err := json.Unmarshal(rawT, t); err != nil { + return err + } + + s.Payload = t + + return nil +} + // SimulationSingleEventCreate: Single event simulations play a single event. type SimulationSingleEventCreate struct { // NotificationSettingID: Paddle ID of the notification setting where this simulation is sent, prefixed with `ntfset_`. @@ -38,7 +95,7 @@ type SimulationSingleEventCreate struct { // Type: Single event sent for this simulation, in the format `entity.event_type`. Type EventTypeName `json:"type,omitempty"` // Payload: Simulation payload. Pass a JSON object that matches the schema for an event type to simulate a custom payload. If omitted, Paddle populates with a demo example. - Payload *Payload `json:"payload,omitempty"` + Payload paddlenotification.NotificationPayload `json:"payload,omitempty"` } // SimulationScenarioType: Scenario for this simulation. Scenario simulations play all events sent for a subscription lifecycle event.. @@ -73,7 +130,7 @@ type SimulationSingleEventUpdate struct { // Type: Single event sent for this simulation, in the format `entity.event_type`. Type EventTypeName `json:"type,omitempty"` // Payload: Simulation payload. Pass a JSON object that matches the schema for an event type to simulate a custom payload. Set to `null` to clear and populate with a demo example. - Payload *Payload `json:"payload,omitempty"` + Payload paddlenotification.NotificationPayload `json:"payload,omitempty"` } // SimulationScenarioUpdate: Scenario simulations play all events sent for a subscription lifecycle event. diff --git a/testdata/simulation.json b/testdata/simulation.json new file mode 100644 index 0000000..d66b3bd --- /dev/null +++ b/testdata/simulation.json @@ -0,0 +1,27 @@ +{ + "data": { + "id": "ntfsim_01j9y0jwekrcyezscgkehvdmd6", + "notification_setting_id": "ntfset_01j84xydheq48n3btebwf6ndn6", + "name": "Go SDK Test with Payload", + "type": "customer.created", + "status": "active", + "payload": { + "id": "ctm_01j870snka0xdp6szgyxze6d6d", + "name": "John Doe", + "email": "john.doe@paddle.com", + "marketing_consent": false, + "status": "active", + "custom_data": null, + "locale": "en", + "created_at": "2024-04-12T00:00:00Z", + "updated_at": "2024-04-12T00:00:00Z", + "import_meta": null + }, + "last_run_at": null, + "created_at": "2024-10-11T14:44:06.739792Z", + "updated_at": "2024-10-11T14:44:06.739792Z" + }, + "meta": { + "request_id": "cba8e79a-e5af-463f-9648-338262c4fd00" + } +} diff --git a/testdata/simulation_run.json b/testdata/simulation_run.json new file mode 100644 index 0000000..f902a8c --- /dev/null +++ b/testdata/simulation_run.json @@ -0,0 +1,12 @@ +{ + "data" : { + "id" : "ntfsimrun_01j9yq3yspewy5r8zr05vkeekd", + "status" : "pending", + "type" : "customer.created", + "created_at" : "2024-10-11T21:17:54.870633Z", + "updated_at" : "2024-10-11T21:17:54.870633Z" + }, + "meta" : { + "request_id" : "4779ce0c-4830-4ab1-b3f2-86cd3bce671d" + } +} diff --git a/testdata/simulation_run_with_events.json b/testdata/simulation_run_with_events.json new file mode 100644 index 0000000..1889555 --- /dev/null +++ b/testdata/simulation_run_with_events.json @@ -0,0 +1,38 @@ +{ + "data" : { + "id" : "ntfsimrun_01j9yq3yspewy5r8zr05vkeekd", + "status" : "completed", + "type" : "customer.created", + "created_at" : "2024-10-11T21:17:54.870633Z", + "updated_at" : "2024-10-11T21:17:55.460363Z", + "events" : [ { + "id" : "ntfsimevt_01j9yq3yt7b418zwfs5pzsefqs", + "status" : "success", + "event_type" : "customer.created", + "payload" : { + "id" : "ctm_01j870snka0xdp6szgyxze6d6d", + "name" : "John Doe", + "email" : "john.doe+blackhole@paddle.com", + "locale" : "en", + "status" : "active", + "created_at" : "2024-04-12T00:00:00Z", + "updated_at" : "2024-04-12T00:00:00Z", + "custom_data" : null, + "import_meta" : null, + "marketing_consent" : false + }, + "request" : { + "body" : "{\"event_id\":\"ntfsimevt_01j9yq3yt7b418zwfs5pzsefqs\",\"event_type\":\"customer.created\",\"occurred_at\":\"2024-10-11T21:17:54.887463Z\",\"notification_id\":\"ntfsimntf_01j9yq3yz7y77gszvatscnqfan\",\"data\":{\"id\":\"ctm_01j870snka0xdp6szgyxze6d6d\",\"name\":\"John Doe\",\"email\":\"john.doe+blackhole@paddle.com\",\"locale\":\"en\",\"status\":\"active\",\"created_at\":\"2024-04-12T00:00:00Z\",\"updated_at\":\"2024-04-12T00:00:00Z\",\"custom_data\":null,\"import_meta\":null,\"marketing_consent\":false}}" + }, + "response" : { + "body" : "{\"status\":\"SUCCESS\",\"message\":\"Request handled by Hookdeck. Check your dashboard to inspect the request: https://dashboard.hookdeck.com/requests/123\",\"request_id\":\"123\"}", + "status_code" : 200 + }, + "created_at" : "2024-10-11T21:17:54.887463Z", + "updated_at" : "2024-10-11T21:17:55.427851Z" + } ] + }, + "meta" : { + "request_id" : "252fb511-bf6e-45bf-b99e-6f6a9533183d" + } +} diff --git a/testdata/simulations.json b/testdata/simulations.json new file mode 100644 index 0000000..8519ce0 --- /dev/null +++ b/testdata/simulations.json @@ -0,0 +1,61 @@ +{ + "data": [ + { + "id": "ntfsim_01j9y0jwekrcyezscgkehvdmd6", + "notification_setting_id": "ntfset_01j84xydheq48n3btebwf6ndn6", + "name": "Go SDK", + "type": "customer.created", + "status": "active", + "payload": { + "id": "ctm_01j870snka0xdp6szgyxze6d6d", + "name": "John Doe", + "email": "john.doe+blackhole@paddle.com", + "locale": "en", + "status": "active", + "created_at": "2024-04-12T00:00:00Z", + "updated_at": "2024-04-12T00:00:00Z", + "custom_data": null, + "import_meta": null, + "marketing_consent": false + }, + "last_run_at": null, + "created_at": "2024-10-11T14:44:06.739792Z", + "updated_at": "2024-10-11T14:44:06.739792Z" + }, + { + "id": "ntfsim_01j9y07xtgjg4nycye6wyp4rb8", + "notification_setting_id": "ntfset_01j84xydheq48n3btebwf6ndn6", + "name": "Go SDK", + "type": "address.created", + "status": "active", + "payload": { + "id": "add_01hv8gq3318ktkfengj2r75gfx", + "city": "New York", + "region": "NY", + "status": "active", + "created_at": "2024-04-12T06:42:58.785Z", + "first_line": "4050 Jefferson Plaza, 41st Floor", + "updated_at": "2024-04-12T06:42:58.785Z", + "custom_data": null, + "customer_id": "ctm_01hv6y1jedq4p1n0yqn5ba3ky4", + "description": "Head Office", + "import_meta": null, + "postal_code": "10021", + "second_line": null, + "country_code": "US" + }, + "last_run_at": null, + "created_at": "2024-10-11T14:38:07.696081Z", + "updated_at": "2024-10-11T14:38:07.696081Z" + } + ], + "meta": { + "pagination": { + "per_page": 50, + "estimated_total": 2, + "next": "https://api.paddle.com/simulations?after=ntfsim_01j9y07xtgjg4nycye6wyp4rb8", + "has_more": false + }, + "request_id": "37c14546-8d1e-4824-8572-ab4b12b9e08a" + } +} From fdb71c857d962f6d0bc51e716193215be83cbcca Mon Sep 17 00:00:00 2001 From: David Grayston Date: Wed, 16 Oct 2024 14:59:07 +0100 Subject: [PATCH 7/9] docs: Add scenario simulation create example --- example_simulations_test.go | 44 +++++++++++++++++++++++++++++++ example_test.go | 1 + testdata/simulation_scenario.json | 16 +++++++++++ 3 files changed, 61 insertions(+) create mode 100644 testdata/simulation_scenario.json diff --git a/example_simulations_test.go b/example_simulations_test.go index 87526db..625d101 100644 --- a/example_simulations_test.go +++ b/example_simulations_test.go @@ -67,6 +67,50 @@ func Example_simulation_create() { //ctm_01j870snka0xdp6szgyxze6d6d } +// Demonstrates how to create a scenario Simulation +func Example_simulation_create_scenario_simulation() { + // Create a mock HTTP server for this example - skip over this bit! + s := mockServerForExample(mockServerResponse{stub: &stub{paths: []stubPath{simulationScenario}}}) + + // Create a new Paddle client. + client, err := paddle.New( + os.Getenv("PADDLE_API_KEY"), + paddle.WithBaseURL(s.URL), // Uses the mock server, you will not need this in your integration. + ) + + if err != nil { + fmt.Println(err) + return + } + + ctx := context.Background() + + // Optionally set a transit ID on the context. This is useful to link your + // own request IDs to Paddle API requests. + ctx = paddle.ContextWithTransitID(ctx, "sdk-testing-request-1") + + simulation, err := client.CreateSimulation(ctx, paddle.NewCreateSimulationRequestSimulationScenarioCreate(&paddle.SimulationScenarioCreate{ + NotificationSettingID: "ntfset_01j84xydheq48n3btebwf6ndn6", + Name: "Go SDK Test without Payload", + Type: "subscription_creation", + })) + if err != nil { + return + } + + fmt.Println(simulation.ID) + fmt.Println(simulation.Type) + fmt.Println(simulation.Name) + fmt.Println(simulation.NotificationSettingID) + fmt.Println(simulation.Payload) + // Output: + //ntfsim_01j9y0jwekrcyezscgkehvdmd6 + //subscription_creation + //Go SDK Test without Payload + //ntfset_01j84xydheq48n3btebwf6ndn6 + // +} + // Demonstrates how to update a Simulation with Payload and read the Payload back out of the response func Example_simulation_update() { // Create a mock HTTP server for this example - skip over this bit! diff --git a/example_test.go b/example_test.go index 3a139b1..2002b12 100644 --- a/example_test.go +++ b/example_test.go @@ -17,6 +17,7 @@ const ( transactionsPaginatedPg2 stubPath = "testdata/transactions_paginated_pg2.json" priceCreatedEvent stubPath = "testdata/price_created.json" simulation stubPath = "testdata/simulation.json" + simulationScenario stubPath = "testdata/simulation_scenario.json" simulations stubPath = "testdata/simulations.json" simulationRun stubPath = "testdata/simulation_run.json" simulationRunWithEvents stubPath = "testdata/simulation_run_with_events.json" diff --git a/testdata/simulation_scenario.json b/testdata/simulation_scenario.json new file mode 100644 index 0000000..f2311d4 --- /dev/null +++ b/testdata/simulation_scenario.json @@ -0,0 +1,16 @@ +{ + "data": { + "id": "ntfsim_01j9y0jwekrcyezscgkehvdmd6", + "notification_setting_id": "ntfset_01j84xydheq48n3btebwf6ndn6", + "name": "Go SDK Test without Payload", + "type": "subscription_creation", + "status": "active", + "payload": null, + "last_run_at": null, + "created_at": "2024-10-11T14:44:06.739792Z", + "updated_at": "2024-10-11T14:44:06.739792Z" + }, + "meta": { + "request_id": "cba8e79a-e5af-463f-9648-338262c4fd00" + } +} From 1dab62ed4015ec4fca300693eeeaf5a7ccbb5e81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Fern=C3=A1ndez=20Saborit?= Date: Wed, 16 Oct 2024 17:55:34 +0200 Subject: [PATCH 8/9] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index efb61f4..5d4f745 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), Check our main [developer changelog](https://developer.paddle.com/?utm_source=dx&utm_medium=paddle-go-sdk) for information about changes to the Paddle Billing platform, the Paddle API, and other developer tools. -## 2.1.0 - 2024-10-15 +## 2.1.0 - 2024-10-16 ### Added From 47c46d158803a7ce608fb78d94b09dd8d53bb4f6 Mon Sep 17 00:00:00 2001 From: davidgrayston-paddle Date: Thu, 17 Oct 2024 15:41:17 +0100 Subject: [PATCH 9/9] docs: Update CHANGELOG date --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d4f745..a4b3966 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), Check our main [developer changelog](https://developer.paddle.com/?utm_source=dx&utm_medium=paddle-go-sdk) for information about changes to the Paddle Billing platform, the Paddle API, and other developer tools. -## 2.1.0 - 2024-10-16 +## 2.1.0 - 2024-10-17 ### Added