From 81feaf16531e69246da3e63867c25b87c5aa7625 Mon Sep 17 00:00:00 2001 From: Ramya Rao <100975018+ramya-stripe@users.noreply.github.com> Date: Tue, 1 Oct 2024 09:33:41 -0700 Subject: [PATCH] Support for APIs in the new API version 2024-09-30.acacia (#1880) --- .vscode/settings.json | 6 +- OPENAPI_VERSION | 2 +- README.md | 25 + src/main/java/com/stripe/ApiVersion.java | 2 +- src/main/java/com/stripe/Stripe.java | 19 +- src/main/java/com/stripe/StripeClient.java | 176 ++++- ...BillingMeterErrorReportTriggeredEvent.java | 89 +++ .../V1BillingMeterNoMeterFoundEvent.java | 76 +++ .../stripe/examples/MeterEventManager.java | 61 ++ .../java/com/stripe/examples/NewExample.java | 36 + .../stripe/examples/StripeWebhookHandler.java | 52 ++ .../exception/AuthenticationException.java | 5 + .../com/stripe/exception/StripeException.java | 33 +- .../TemporarySessionExpiredException.java | 34 + .../java/com/stripe/model/Capability.java | 6 +- .../java/com/stripe/model/CreditNote.java | 73 +++ .../com/stripe/model/CreditNoteLineItem.java | 73 +++ src/main/java/com/stripe/model/Customer.java | 9 +- src/main/java/com/stripe/model/Event.java | 3 +- .../stripe/model/EventDataClassLookup.java | 11 +- .../model/EventDataObjectDeserializer.java | 9 +- .../com/stripe/model/InstantDeserializer.java | 32 + .../com/stripe/model/InstantSerializer.java | 19 + src/main/java/com/stripe/model/Invoice.java | 97 +++ .../com/stripe/model/InvoiceLineItem.java | 97 +++ src/main/java/com/stripe/model/Margin.java | 72 ++ .../java/com/stripe/model/PromotionCode.java | 3 +- .../com/stripe/model/StripeCollection.java | 6 +- .../java/com/stripe/model/StripeError.java | 4 + .../java/com/stripe/model/StripeObject.java | 22 +- .../com/stripe/model/StripeSearchResult.java | 2 + .../java/com/stripe/model/Subscription.java | 90 +-- src/main/java/com/stripe/model/TODO.java | 7 + src/main/java/com/stripe/model/ThinEvent.java | 34 + .../stripe/model/ThinEventRelatedObject.java | 16 + .../java/com/stripe/model/billing/Alert.java | 85 +-- .../model/billing/CreditBalanceSummary.java | 191 ++++++ .../billing/CreditBalanceTransaction.java | 346 ++++++++++ .../CreditBalanceTransactionCollection.java | 7 + .../com/stripe/model/billing/CreditGrant.java | 448 +++++++++++++ .../model/billing/CreditGrantCollection.java | 6 + .../java/com/stripe/model/tax/Settings.java | 3 +- .../stripe/model/treasury/ReceivedCredit.java | 3 +- src/main/java/com/stripe/model/v2/Event.java | 154 +++++ .../stripe/model/v2/EventDataClassLookup.java | 35 + .../model/v2/EventTypeAdapterFactory.java | 59 ++ .../com/stripe/model/v2/StripeCollection.java | 173 +++++ .../stripe/model/v2/StripeDeletedObject.java | 22 + .../stripe/model/v2/billing/MeterEvent.java | 61 ++ .../v2/billing/MeterEventAdjustment.java | 77 +++ .../model/v2/billing/MeterEventSession.java | 51 ++ src/main/java/com/stripe/net/ApiMode.java | 2 +- src/main/java/com/stripe/net/ApiRequest.java | 16 +- .../java/com/stripe/net/ApiRequestParams.java | 2 +- .../stripe/net/ApiRequestParamsConverter.java | 3 + src/main/java/com/stripe/net/ApiResource.java | 10 +- .../java/com/stripe/net/Authenticator.java | 15 + src/main/java/com/stripe/net/BaseAddress.java | 4 +- .../stripe/net/BearerTokenAuthenticator.java | 46 ++ src/main/java/com/stripe/net/FormEncoder.java | 47 +- .../GlobalStripeResponseGetterOptions.java | 17 +- src/main/java/com/stripe/net/HttpContent.java | 26 + src/main/java/com/stripe/net/JsonEncoder.java | 23 + .../stripe/net/LiveStripeResponseGetter.java | 170 ++++- src/main/java/com/stripe/net/OAuth.java | 1 - .../java/com/stripe/net/RawApiRequest.java | 51 ++ .../com/stripe/net/RawRequestOptions.java | 144 ++++ .../java/com/stripe/net/RequestOptions.java | 136 +++- .../net/RequestSigningAuthenticator.java | 167 +++++ ...tripeCollectionItemTypeSettingFactory.java | 2 + .../java/com/stripe/net/StripeRequest.java | 233 +++++-- .../com/stripe/net/StripeResponseGetter.java | 5 + .../net/StripeResponseGetterOptions.java | 6 +- src/main/java/com/stripe/net/Webhook.java | 2 +- .../com/stripe/param/ProductCreateParams.java | 155 ++++- .../param/PromotionCodeCreateParams.java | 10 +- .../param/WebhookEndpointCreateParams.java | 5 +- .../param/billing/AlertCreateParams.java | 283 ++++---- .../CreditBalanceSummaryRetrieveParams.java | 332 ++++++++++ .../CreditBalanceTransactionListParams.java | 203 ++++++ ...reditBalanceTransactionRetrieveParams.java | 99 +++ .../billing/CreditGrantCreateParams.java | 620 ++++++++++++++++++ .../billing/CreditGrantExpireParams.java | 98 +++ .../param/billing/CreditGrantListParams.java | 188 ++++++ .../billing/CreditGrantRetrieveParams.java | 98 +++ .../billing/CreditGrantUpdateParams.java | 168 +++++ .../billing/CreditGrantVoidGrantParams.java | 98 +++ .../ConfigurationCreateParams.java | 26 +- .../param/checkout/SessionCreateParams.java | 6 +- .../ReaderProcessPaymentIntentParams.java | 47 +- .../ReaderProcessSetupIntentParams.java | 52 +- .../MeterEventAdjustmentCreateParams.java | 203 ++++++ .../v2/billing/MeterEventCreateParams.java | 166 +++++ .../billing/MeterEventStreamCreateParams.java | 259 ++++++++ .../stripe/param/v2/core/EventListParams.java | 103 +++ .../com/stripe/service/BillingService.java | 12 + .../stripe/service/SubscriptionService.java | 72 +- .../java/com/stripe/service/V2Services.java | 19 + .../billing/CreditBalanceSummaryService.java | 38 ++ .../CreditBalanceTransactionService.java | 71 ++ .../service/billing/CreditGrantService.java | 170 +++++ .../com/stripe/service/v2/BillingService.java | 27 + .../com/stripe/service/v2/CoreService.java | 15 + .../billing/MeterEventAdjustmentService.java | 38 ++ .../service/v2/billing/MeterEventService.java | 45 ++ .../v2/billing/MeterEventSessionService.java | 37 ++ .../v2/billing/MeterEventStreamService.java | 47 ++ .../stripe/service/v2/core/EventService.java | 50 ++ src/main/java/com/stripe/v2/Amount.java | 22 + .../java/com/stripe/v2/EmptyStripeObject.java | 10 + src/test/java/com/stripe/BaseStripeTest.java | 9 +- .../java/com/stripe/DocumentationTest.java | 89 --- src/test/java/com/stripe/RawRequestTest.java | 336 ++++++++++ .../java/com/stripe/StripeClientTest.java | 181 +++++ .../java/com/stripe/functional/ErrorTest.java | 96 ++- .../stripe/functional/GeneratedExamples.java | 60 +- .../LiveStripeResponseGetterTest.java | 23 +- .../functional/StripeResponseStreamTest.java | 15 +- .../com/stripe/functional/TimeoutTest.java | 3 +- .../functional/v2/StripeCollectionTest.java | 233 +++++++ .../stripe/model/InstantDeserializerTest.java | 34 + .../stripe/model/InstantSerializerTest.java | 53 ++ .../com/stripe/model/PagingIteratorTest.java | 1 + .../model/SearchPagingIteratorTest.java | 1 + .../com/stripe/model/StandardizationTest.java | 156 +++++ .../java/com/stripe/model/v2/EventTests.java | 162 +++++ .../net/ApiRequestParamsConverterTest.java | 19 + .../com/stripe/net/ApiRequestParamsTest.java | 2 +- .../com/stripe/net/ClientOptionsTest.java | 4 + .../java/com/stripe/net/FormEncoderTest.java | 24 +- .../java/com/stripe/net/HttpClientTest.java | 5 +- .../java/com/stripe/net/HttpContentTest.java | 10 +- .../java/com/stripe/net/JsonEncoderTest.java | 67 ++ .../com/stripe/net/RequestOptionsTest.java | 9 +- .../net/RequestSigningAuthenticatorTest.java | 128 ++++ .../com/stripe/net/StripeRequestTest.java | 200 ++++-- .../net/TestStripeResponseGetterOptions.java | 14 +- src/test/java/com/stripe/net/WebhookTest.java | 8 +- src/test/java/com/stripe/v2/AmountTest.java | 35 + src/test/java/com/stripe/v2/InstantTest.java | 38 ++ .../resources/api_fixtures/billing_meter.json | 25 + ...2_outbound_payment_insufficient_funds.json | 8 + .../external_account_collection.json | 3 +- 143 files changed, 9385 insertions(+), 708 deletions(-) create mode 100644 src/main/java/com/stripe/events/V1BillingMeterErrorReportTriggeredEvent.java create mode 100644 src/main/java/com/stripe/events/V1BillingMeterNoMeterFoundEvent.java create mode 100644 src/main/java/com/stripe/examples/MeterEventManager.java create mode 100644 src/main/java/com/stripe/examples/NewExample.java create mode 100644 src/main/java/com/stripe/examples/StripeWebhookHandler.java create mode 100644 src/main/java/com/stripe/exception/TemporarySessionExpiredException.java create mode 100644 src/main/java/com/stripe/model/InstantDeserializer.java create mode 100644 src/main/java/com/stripe/model/InstantSerializer.java create mode 100644 src/main/java/com/stripe/model/Margin.java create mode 100644 src/main/java/com/stripe/model/TODO.java create mode 100644 src/main/java/com/stripe/model/ThinEvent.java create mode 100644 src/main/java/com/stripe/model/ThinEventRelatedObject.java create mode 100644 src/main/java/com/stripe/model/billing/CreditBalanceSummary.java create mode 100644 src/main/java/com/stripe/model/billing/CreditBalanceTransaction.java create mode 100644 src/main/java/com/stripe/model/billing/CreditBalanceTransactionCollection.java create mode 100644 src/main/java/com/stripe/model/billing/CreditGrant.java create mode 100644 src/main/java/com/stripe/model/billing/CreditGrantCollection.java create mode 100644 src/main/java/com/stripe/model/v2/Event.java create mode 100644 src/main/java/com/stripe/model/v2/EventDataClassLookup.java create mode 100644 src/main/java/com/stripe/model/v2/EventTypeAdapterFactory.java create mode 100644 src/main/java/com/stripe/model/v2/StripeCollection.java create mode 100644 src/main/java/com/stripe/model/v2/StripeDeletedObject.java create mode 100644 src/main/java/com/stripe/model/v2/billing/MeterEvent.java create mode 100644 src/main/java/com/stripe/model/v2/billing/MeterEventAdjustment.java create mode 100644 src/main/java/com/stripe/model/v2/billing/MeterEventSession.java create mode 100644 src/main/java/com/stripe/net/Authenticator.java create mode 100644 src/main/java/com/stripe/net/BearerTokenAuthenticator.java create mode 100644 src/main/java/com/stripe/net/JsonEncoder.java create mode 100644 src/main/java/com/stripe/net/RawApiRequest.java create mode 100644 src/main/java/com/stripe/net/RawRequestOptions.java create mode 100644 src/main/java/com/stripe/net/RequestSigningAuthenticator.java create mode 100644 src/main/java/com/stripe/param/billing/CreditBalanceSummaryRetrieveParams.java create mode 100644 src/main/java/com/stripe/param/billing/CreditBalanceTransactionListParams.java create mode 100644 src/main/java/com/stripe/param/billing/CreditBalanceTransactionRetrieveParams.java create mode 100644 src/main/java/com/stripe/param/billing/CreditGrantCreateParams.java create mode 100644 src/main/java/com/stripe/param/billing/CreditGrantExpireParams.java create mode 100644 src/main/java/com/stripe/param/billing/CreditGrantListParams.java create mode 100644 src/main/java/com/stripe/param/billing/CreditGrantRetrieveParams.java create mode 100644 src/main/java/com/stripe/param/billing/CreditGrantUpdateParams.java create mode 100644 src/main/java/com/stripe/param/billing/CreditGrantVoidGrantParams.java create mode 100644 src/main/java/com/stripe/param/v2/billing/MeterEventAdjustmentCreateParams.java create mode 100644 src/main/java/com/stripe/param/v2/billing/MeterEventCreateParams.java create mode 100644 src/main/java/com/stripe/param/v2/billing/MeterEventStreamCreateParams.java create mode 100644 src/main/java/com/stripe/param/v2/core/EventListParams.java create mode 100644 src/main/java/com/stripe/service/V2Services.java create mode 100644 src/main/java/com/stripe/service/billing/CreditBalanceSummaryService.java create mode 100644 src/main/java/com/stripe/service/billing/CreditBalanceTransactionService.java create mode 100644 src/main/java/com/stripe/service/billing/CreditGrantService.java create mode 100644 src/main/java/com/stripe/service/v2/BillingService.java create mode 100644 src/main/java/com/stripe/service/v2/CoreService.java create mode 100644 src/main/java/com/stripe/service/v2/billing/MeterEventAdjustmentService.java create mode 100644 src/main/java/com/stripe/service/v2/billing/MeterEventService.java create mode 100644 src/main/java/com/stripe/service/v2/billing/MeterEventSessionService.java create mode 100644 src/main/java/com/stripe/service/v2/billing/MeterEventStreamService.java create mode 100644 src/main/java/com/stripe/service/v2/core/EventService.java create mode 100644 src/main/java/com/stripe/v2/Amount.java create mode 100644 src/main/java/com/stripe/v2/EmptyStripeObject.java create mode 100644 src/test/java/com/stripe/RawRequestTest.java create mode 100644 src/test/java/com/stripe/functional/v2/StripeCollectionTest.java create mode 100644 src/test/java/com/stripe/model/InstantDeserializerTest.java create mode 100644 src/test/java/com/stripe/model/InstantSerializerTest.java create mode 100644 src/test/java/com/stripe/model/StandardizationTest.java create mode 100644 src/test/java/com/stripe/model/v2/EventTests.java create mode 100644 src/test/java/com/stripe/net/JsonEncoderTest.java create mode 100644 src/test/java/com/stripe/net/RequestSigningAuthenticatorTest.java create mode 100644 src/test/java/com/stripe/v2/AmountTest.java create mode 100644 src/test/java/com/stripe/v2/InstantTest.java create mode 100644 src/test/resources/api_fixtures/billing_meter.json create mode 100644 src/test/resources/api_fixtures/error_v2_outbound_payment_insufficient_funds.json diff --git a/.vscode/settings.json b/.vscode/settings.json index c7082cd1373..61172939b09 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -18,5 +18,9 @@ }, "java.configuration.updateBuildConfiguration": "automatic", // LSP was ooming and it recommended this change - "java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx2G -Xms100m -Xlog:disable" + "java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx2G -Xms100m -Xlog:disable", + "java.test.config": { + "vmargs": [ "-Dstripe.disallowGlobalResponseGetterFallback=true"] + + } } diff --git a/OPENAPI_VERSION b/OPENAPI_VERSION index 5f5b311191b..8f166ae2e06 100644 --- a/OPENAPI_VERSION +++ b/OPENAPI_VERSION @@ -1 +1 @@ -v1267 \ No newline at end of file +v1268 \ No newline at end of file diff --git a/README.md b/README.md index d6a5993561d..e92687826a0 100644 --- a/README.md +++ b/README.md @@ -262,6 +262,31 @@ If your beta feature requires a `Stripe-Version` header to be sent, set the `Str Stripe.addBetaVersion("feature_beta", "v3"); ``` +### Custom requests + +If you would like to send a request to an undocumented API (for example you are in a private beta), or if you prefer to bypass the method definitions in the library and specify your request details directly, you can use the `rawRequest` method on `StripeClient`. + +```java +// Create a RawRequestOptions object, allowing you to set per-request +// configuration options like additional headers. +Map stripeVersionHeader = new HashMap<>(); +stripeVersionHeader.put("Stripe-Version", "2022-11-15; feature_beta=v3"); +RawRequestOptions options = + RawRequestOptions.builder() + .setAdditionalHeaders(stripeVersionHeader) + .build(); + +// Make the request using the Stripe.rawRequest() method. +StripeClient client = new StripeClient("sk_test_..."); +final StripeResponse response = + client.rawRequest( + ApiResource.RequestMethod.POST, "/v1/beta_endpoint", "param=123", options); + +// (Optional) response.body() is a string. You can call +// Stripe.deserialize() to get a StripeObject. +StripeObject obj = client.deserialize(response.body()); +``` + ## Support New features and bug fixes are released on the latest major version of the Stripe Java client library. If you are on an older major version, we recommend that you upgrade to the latest in order to use the new features and bug fixes including those for security vulnerabilities. Older major versions of the package will continue to be available for use, but will not be receiving any updates. diff --git a/src/main/java/com/stripe/ApiVersion.java b/src/main/java/com/stripe/ApiVersion.java index df7e1a4f5d7..f10ace355e9 100644 --- a/src/main/java/com/stripe/ApiVersion.java +++ b/src/main/java/com/stripe/ApiVersion.java @@ -2,5 +2,5 @@ package com.stripe; final class ApiVersion { - public static final String CURRENT = "2024-06-20"; + public static final String CURRENT = "2024-09-30.acacia"; } diff --git a/src/main/java/com/stripe/Stripe.java b/src/main/java/com/stripe/Stripe.java index 8ad8117d230..5443281488f 100644 --- a/src/main/java/com/stripe/Stripe.java +++ b/src/main/java/com/stripe/Stripe.java @@ -13,12 +13,12 @@ public abstract class Stripe { public static final String CONNECT_API_BASE = "https://connect.stripe.com"; public static final String LIVE_API_BASE = "https://api.stripe.com"; public static final String UPLOAD_API_BASE = "https://files.stripe.com"; + public static final String METER_EVENTS_API_BASE = "https://meter-events.stripe.com"; public static final String VERSION = "26.12.0"; public static volatile String apiKey; public static volatile String clientId; public static volatile boolean enableTelemetry = true; - public static volatile String partnerId; // Note that URLConnection reserves the value of 0 to mean "infinite // timeout", so we use -1 here to represent an unset value which should @@ -26,14 +26,14 @@ public abstract class Stripe { private static volatile int connectTimeout = -1; private static volatile int readTimeout = -1; - private static volatile int maxNetworkRetries = 0; + private static volatile int maxNetworkRetries = 2; private static volatile String apiBase = LIVE_API_BASE; private static volatile String connectBase = CONNECT_API_BASE; private static volatile String uploadBase = UPLOAD_API_BASE; + private static volatile String meterEventsBase = METER_EVENTS_API_BASE; private static volatile Proxy connectionProxy = null; private static volatile PasswordAuthentication proxyCredential = null; - private static volatile Map appInfo = null; /** @@ -72,6 +72,18 @@ public static String getUploadBase() { return uploadBase; } + /** + * (FOR TESTING ONLY) If you'd like your events requests to hit your own (mocked) server, you can + * set this up here by overriding the base api URL. + */ + public static void overrideMeterEventsBase(final String overriddenMeterEventsBase) { + meterEventsBase = overriddenMeterEventsBase; + } + + public static String getMeterEventsBase() { + return meterEventsBase; + } + /** * Set proxy to tunnel all Stripe connections. * @@ -94,6 +106,7 @@ public static int getConnectTimeout() { if (connectTimeout == -1) { return DEFAULT_CONNECT_TIMEOUT; } + return connectTimeout; } diff --git a/src/main/java/com/stripe/StripeClient.java b/src/main/java/com/stripe/StripeClient.java index d000e326d13..483d5f3f96d 100644 --- a/src/main/java/com/stripe/StripeClient.java +++ b/src/main/java/com/stripe/StripeClient.java @@ -1,8 +1,11 @@ package com.stripe; import com.stripe.exception.SignatureVerificationException; -import com.stripe.model.Event; +import com.stripe.exception.StripeException; +import com.stripe.model.StripeObject; +import com.stripe.model.ThinEvent; import com.stripe.net.*; +import com.stripe.net.Webhook.Signature; import java.net.PasswordAuthentication; import java.net.Proxy; import lombok.Getter; @@ -38,6 +41,24 @@ protected StripeResponseGetter getResponseGetter() { return responseGetter; } + /** + * Returns an StripeEvent instance using the provided JSON payload. Throws a JsonSyntaxException + * if the payload is not valid JSON, and a SignatureVerificationException if the signature + * verification fails for any reason. + * + * @param payload the payload sent by Stripe. + * @param sigHeader the contents of the signature header sent by Stripe. + * @param secret secret used to generate the signature. + * @return the StripeEvent instance + * @throws SignatureVerificationException if the verification fails. + */ + public ThinEvent parseThinEvent(String payload, String sigHeader, String secret) + throws SignatureVerificationException { + Signature.verifyHeader(payload, sigHeader, secret, Webhook.DEFAULT_TOLERANCE); + + return ApiResource.GSON.fromJson(payload, ThinEvent.class); + } + /** * Returns an Event instance using the provided JSON payload. Throws a JsonSyntaxException if the * payload is not valid JSON, and a SignatureVerificationException if the signature verification @@ -49,9 +70,9 @@ protected StripeResponseGetter getResponseGetter() { * @return the Event instance * @throws SignatureVerificationException if the verification fails. */ - public Event constructEvent(String payload, String sigHeader, String secret) + public com.stripe.model.Event parseSnapshotEvent(String payload, String sigHeader, String secret) throws SignatureVerificationException { - Event event = Webhook.constructEvent(payload, sigHeader, secret); + com.stripe.model.Event event = Webhook.constructEvent(payload, sigHeader, secret); event.setResponseGetter(this.getResponseGetter()); return event; } @@ -69,9 +90,10 @@ public Event constructEvent(String payload, String sigHeader, String secret) * @return the Event instance * @throws SignatureVerificationException if the verification fails. */ - public Event constructEvent(String payload, String sigHeader, String secret, long tolerance) + public com.stripe.model.Event parseSnapshotEvent( + String payload, String sigHeader, String secret, long tolerance) throws SignatureVerificationException { - Event event = Webhook.constructEvent(payload, sigHeader, secret, tolerance); + com.stripe.model.Event event = Webhook.constructEvent(payload, sigHeader, secret, tolerance); event.setResponseGetter(this.getResponseGetter()); return event; } @@ -345,6 +367,10 @@ public com.stripe.service.TreasuryService treasury() { return new com.stripe.service.TreasuryService(this.getResponseGetter()); } + public com.stripe.service.V2Services v2() { + return new com.stripe.service.V2Services(this.getResponseGetter()); + } + public com.stripe.service.WebhookEndpointService webhookEndpoints() { return new com.stripe.service.WebhookEndpointService(this.getResponseGetter()); } @@ -354,7 +380,7 @@ static class ClientStripeResponseGetterOptions extends StripeResponseGetterOptio // When adding setting here keep them in sync with settings in RequestOptions and // in the RequestOptions.merge method @Getter(onMethod_ = {@Override}) - private final String apiKey; + private final Authenticator authenticator; @Getter(onMethod_ = {@Override}) private final String clientId; @@ -383,8 +409,14 @@ static class ClientStripeResponseGetterOptions extends StripeResponseGetterOptio @Getter(onMethod_ = {@Override}) private final String connectBase; + @Getter(onMethod_ = {@Override}) + private final String meterEventsBase; + + @Getter(onMethod_ = {@Override}) + private final String stripeContext; + ClientStripeResponseGetterOptions( - String apiKey, + Authenticator authenticator, String clientId, int connectTimeout, int readTimeout, @@ -393,8 +425,10 @@ static class ClientStripeResponseGetterOptions extends StripeResponseGetterOptio PasswordAuthentication proxyCredential, String apiBase, String filesBase, - String connectBase) { - this.apiKey = apiKey; + String connectBase, + String meterEventsBase, + String stripeContext) { + this.authenticator = authenticator; this.clientId = clientId; this.connectTimeout = connectTimeout; this.readTimeout = readTimeout; @@ -404,6 +438,8 @@ static class ClientStripeResponseGetterOptions extends StripeResponseGetterOptio this.apiBase = apiBase; this.filesBase = filesBase; this.connectBase = connectBase; + this.meterEventsBase = meterEventsBase; + this.stripeContext = stripeContext; } } @@ -416,7 +452,7 @@ public static StripeClientBuilder builder() { } public static final class StripeClientBuilder { - private String apiKey; + private Authenticator authenticator; private String clientId; private int connectTimeout = Stripe.DEFAULT_CONNECT_TIMEOUT; private int readTimeout = Stripe.DEFAULT_READ_TIMEOUT; @@ -426,6 +462,8 @@ public static final class StripeClientBuilder { private String apiBase = Stripe.LIVE_API_BASE; private String filesBase = Stripe.UPLOAD_API_BASE; private String connectBase = Stripe.CONNECT_API_BASE; + private String meterEventsBase = Stripe.METER_EVENTS_API_BASE; + private String stripeContext; /** * Constructs a request options builder with the global parameters (API key and client ID) as @@ -433,17 +471,34 @@ public static final class StripeClientBuilder { */ public StripeClientBuilder() {} + public Authenticator getAuthenticator() { + return this.authenticator; + } + + public StripeClientBuilder setAuthenticator(Authenticator authenticator) { + this.authenticator = authenticator; + return this; + } + public String getApiKey() { - return this.apiKey; + if (authenticator instanceof BearerTokenAuthenticator) { + return ((BearerTokenAuthenticator) authenticator).getApiKey(); + } + + return null; } - /** - * Set API key to use for authenticating requests. - * - * @param apiKey API key - */ public StripeClientBuilder setApiKey(String apiKey) { - this.apiKey = apiKey; + if (apiKey == null) { + this.authenticator = null; + } else { + this.authenticator = new BearerTokenAuthenticator(apiKey); + } + return this; + } + + public StripeClientBuilder clearApiKey() { + this.authenticator = null; return this; } @@ -579,18 +634,42 @@ public String getConnectBase() { return this.connectBase; } - /** Constructs a {@link StripeClient} with the specified configuration. */ + /** + * Set the base URL for the Stripe Meter Events API. By default this is + * "https://events.stripe.com". + * + *

This only affects requests made with a {@link com.stripe.net.BaseAddress} of EVENTMES. + */ + public StripeClientBuilder setMeterEventsBase(String address) { + this.meterEventsBase = address; + return this; + } + + public String getMeterEventsBase() { + return this.meterEventsBase; + } + + public StripeClientBuilder setStripeContext(String context) { + this.stripeContext = context; + return this; + } + + public String getStripeContext() { + return this.stripeContext; + } + + /** Constructs a {@link StripeResponseGetterOptions} with the specified values. */ public StripeClient build() { return new StripeClient(new LiveStripeResponseGetter(buildOptions(), null)); } StripeResponseGetterOptions buildOptions() { - if (this.apiKey == null) { + if (this.authenticator == null) { throw new IllegalArgumentException( - "No API key provided. Use setApiKey to set the Stripe API key"); + "No authentication settings provided. Use setApiKey to set the Stripe API key"); } return new ClientStripeResponseGetterOptions( - this.apiKey, + this.authenticator, this.clientId, connectTimeout, readTimeout, @@ -599,7 +678,60 @@ StripeResponseGetterOptions buildOptions() { proxyCredential, apiBase, filesBase, - connectBase); + connectBase, + meterEventsBase, + this.stripeContext); } } + + /** + * Send raw request to Stripe API. This is the lowest level method for interacting with the Stripe + * API. This method is useful for interacting with endpoints that are not covered yet in + * stripe-java. + * + * @param method the HTTP method + * @param relativeUrl the relative URL of the request, e.g. "/v1/charges" + * @param content the body of the request as a string + * @return the JSON response as a string + */ + public StripeResponse rawRequest( + final ApiResource.RequestMethod method, final String relativeUrl, final String content) + throws StripeException { + return rawRequest(method, relativeUrl, content, null); + } + + /** + * Send raw request to Stripe API. This is the lowest level method for interacting with the Stripe + * API. This method is useful for interacting with endpoints that are not covered yet in + * stripe-java. + * + * @param method the HTTP method + * @param relativeUrl the relative URL of the request, e.g. "/v1/charges" + * @param content the body of the request as a string + * @param options the special modifiers of the request + * @return the JSON response as a string + */ + public StripeResponse rawRequest( + final ApiResource.RequestMethod method, + final String relativeUrl, + final String content, + RawRequestOptions options) + throws StripeException { + if (options == null) { + options = RawRequestOptions.builder().build(); + } + if (method != ApiResource.RequestMethod.POST && content != null && !content.equals("")) { + throw new IllegalArgumentException( + "content is not allowed for non-POST requests. Please pass null and add request parameters to the query string of the URL."); + } + RawApiRequest req = new RawApiRequest(BaseAddress.API, method, relativeUrl, content, options); + req = req.addUsage("stripe_client"); + req = req.addUsage("raw_request"); + return this.getResponseGetter().rawRequest(req); + } + + /** Deserializes StripeResponse returned by rawRequest into a similar class. */ + public StripeObject deserialize(String rawJson, ApiMode apiMode) throws StripeException { + return StripeObject.deserializeStripeObject(rawJson, this.getResponseGetter(), apiMode); + } } diff --git a/src/main/java/com/stripe/events/V1BillingMeterErrorReportTriggeredEvent.java b/src/main/java/com/stripe/events/V1BillingMeterErrorReportTriggeredEvent.java new file mode 100644 index 00000000000..0d70dcb7f5a --- /dev/null +++ b/src/main/java/com/stripe/events/V1BillingMeterErrorReportTriggeredEvent.java @@ -0,0 +1,89 @@ +// File generated from our OpenAPI spec +package com.stripe.events; + +import com.google.gson.annotations.SerializedName; +import com.stripe.exception.StripeException; +import com.stripe.model.billing.Meter; +import com.stripe.model.v2.Event; +import java.time.Instant; +import java.util.List; +import lombok.Getter; +import lombok.Setter; + +@Getter +public final class V1BillingMeterErrorReportTriggeredEvent extends Event { + /** Data for the v1.billing.meter.error_report_triggered event. */ + @SerializedName("data") + V1BillingMeterErrorReportTriggeredEvent.EventData data; + + @Getter + @Setter + public static final class EventData { + /** Extra field included in the event's {@code data} when fetched from /v2/events. */ + @SerializedName("developer_message_summary") + String developerMessageSummary; + /** This contains information about why meter error happens. */ + @SerializedName("reason") + Reason reason; + /** The end of the window that is encapsulated by this summary. */ + @SerializedName("validation_end") + Instant validationEnd; + /** The start of the window that is encapsulated by this summary. */ + @SerializedName("validation_start") + Instant validationStart; + + public static final class Reason { + /** The total error count within this window. */ + @SerializedName("error_count") + Integer errorCount; + /** The error details. */ + @SerializedName("error_types") + List errorTypes; + + public static final class ErrorType { + /** + * Open Enum. + * + *

One of {@code archived_meter}, {@code meter_event_customer_not_found}, {@code + * meter_event_dimension_count_too_high}, {@code meter_event_invalid_value}, {@code + * meter_event_no_customer_defined}, {@code missing_dimension_payload_keys}, {@code + * no_meter}, {@code timestamp_in_future}, or {@code timestamp_too_far_in_past}. + */ + @SerializedName("code") + String code; + /** The number of errors of this type. */ + @SerializedName("error_count") + Integer errorCount; + /** A list of sample errors of this type. */ + @SerializedName("sample_errors") + List + sampleErrors; + + public static final class SampleError { + /** The error message. */ + @SerializedName("error_message") + String errorMessage; + /** The request causes the error. */ + @SerializedName("request") + Request request; + + public static final class Request { + /** The request idempotency key. */ + @SerializedName("identifier") + String identifier; + } + } + } + } + } + + @SerializedName("related_object") + + /** Object containing the reference to API resource relevant to the event. */ + RelatedObject relatedObject; + + /** Retrieves the related object from the API. Make an API request on every call. */ + public Meter fetchRelatedObject() throws StripeException { + return (Meter) super.fetchRelatedObject(this.relatedObject); + } +} diff --git a/src/main/java/com/stripe/events/V1BillingMeterNoMeterFoundEvent.java b/src/main/java/com/stripe/events/V1BillingMeterNoMeterFoundEvent.java new file mode 100644 index 00000000000..e896ebe7687 --- /dev/null +++ b/src/main/java/com/stripe/events/V1BillingMeterNoMeterFoundEvent.java @@ -0,0 +1,76 @@ +// File generated from our OpenAPI spec +package com.stripe.events; + +import com.google.gson.annotations.SerializedName; +import com.stripe.model.v2.Event; +import java.time.Instant; +import java.util.List; +import lombok.Getter; +import lombok.Setter; + +@Getter +public final class V1BillingMeterNoMeterFoundEvent extends Event { + /** Data for the v1.billing.meter.no_meter_found event. */ + @SerializedName("data") + V1BillingMeterNoMeterFoundEvent.EventData data; + + @Getter + @Setter + public static final class EventData { + /** Extra field included in the event's {@code data} when fetched from /v2/events. */ + @SerializedName("developer_message_summary") + String developerMessageSummary; + /** This contains information about why meter error happens. */ + @SerializedName("reason") + Reason reason; + /** The end of the window that is encapsulated by this summary. */ + @SerializedName("validation_end") + Instant validationEnd; + /** The start of the window that is encapsulated by this summary. */ + @SerializedName("validation_start") + Instant validationStart; + + public static final class Reason { + /** The total error count within this window. */ + @SerializedName("error_count") + Integer errorCount; + /** The error details. */ + @SerializedName("error_types") + List errorTypes; + + public static final class ErrorType { + /** + * Open Enum. + * + *

One of {@code archived_meter}, {@code meter_event_customer_not_found}, {@code + * meter_event_dimension_count_too_high}, {@code meter_event_invalid_value}, {@code + * meter_event_no_customer_defined}, {@code missing_dimension_payload_keys}, {@code + * no_meter}, {@code timestamp_in_future}, or {@code timestamp_too_far_in_past}. + */ + @SerializedName("code") + String code; + /** The number of errors of this type. */ + @SerializedName("error_count") + Integer errorCount; + /** A list of sample errors of this type. */ + @SerializedName("sample_errors") + List sampleErrors; + + public static final class SampleError { + /** The error message. */ + @SerializedName("error_message") + String errorMessage; + /** The request causes the error. */ + @SerializedName("request") + Request request; + + public static final class Request { + /** The request idempotency key. */ + @SerializedName("identifier") + String identifier; + } + } + } + } + } +} diff --git a/src/main/java/com/stripe/examples/MeterEventManager.java b/src/main/java/com/stripe/examples/MeterEventManager.java new file mode 100644 index 00000000000..9f9e142bf33 --- /dev/null +++ b/src/main/java/com/stripe/examples/MeterEventManager.java @@ -0,0 +1,61 @@ +package com.stripe.examples; + +import com.stripe.StripeClient; +import com.stripe.model.v2.billing.MeterEventSession; +import com.stripe.param.v2.billing.MeterEventStreamCreateParams; +import java.time.Instant; + +public class MeterEventManager { + + private String apiKey; + private MeterEventSession meterEventSession; + + public MeterEventManager(String apiKey) { + this.apiKey = apiKey; + } + + @SuppressWarnings("CatchAndPrintStackTrace") + private void refreshMeterEventSession() { + if (meterEventSession == null || meterEventSession.getExpiresAt().isBefore(Instant.now())) { + // Create a new meter event session in case the existing session expired + try { + StripeClient client = new StripeClient(apiKey); + meterEventSession = client.v2().billing().meterEventSession().create(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @SuppressWarnings("CatchAndPrintStackTrace") + public void sendMeterEvent(String eventName, String stripeCustomerId, String value) { + // Refresh the meter event session, if necessary + refreshMeterEventSession(); + + // Create a meter event + + MeterEventStreamCreateParams.Event eventParams = + MeterEventStreamCreateParams.Event.builder() + .setEventName(eventName) + .putPayload("stripe_customer_id", stripeCustomerId) + .putPayload("value", value) + .build(); + MeterEventStreamCreateParams params = + MeterEventStreamCreateParams.builder().addEvent(eventParams).build(); + + try { + StripeClient client = new StripeClient(meterEventSession.getAuthenticationToken()); + client.v2().billing().meterEventStream().create(params); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void main(String[] args) { + String apiKey = "{{API_KEY}}"; + String customerId = "{{CUSTOMER_ID}}"; // Replace with actual customer ID + + MeterEventManager manager = new MeterEventManager(apiKey); + manager.sendMeterEvent("alpaca_ai_tokens", customerId, "28"); + } +} diff --git a/src/main/java/com/stripe/examples/NewExample.java b/src/main/java/com/stripe/examples/NewExample.java new file mode 100644 index 00000000000..908307b1601 --- /dev/null +++ b/src/main/java/com/stripe/examples/NewExample.java @@ -0,0 +1,36 @@ +package com.stripe.examples; + +/** + * To create a new example, clone this file and implement your example. + * + *

To run from VS Code: 1. make sure the recommended extensions are installed 2. right click on + * your new file in the Explorer 3. click Run Java or Debug Java 4. witness greatness. + */ +public class NewExample { + + @SuppressWarnings("unused") + private String apiKey; + + public NewExample(String apiKey) { + this.apiKey = apiKey; + } + + @SuppressWarnings("CatchAndPrintStackTrace") + public void doSomethingGreat() { + + try { + System.out.println("Hello World"); + // StripeClient client = new StripeClient(this.apiKey); + // client.v2().... + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void main(String[] args) { + String apiKey = "{{API_KEY}}"; + + NewExample example = new NewExample(apiKey); + example.doSomethingGreat(); + } +} diff --git a/src/main/java/com/stripe/examples/StripeWebhookHandler.java b/src/main/java/com/stripe/examples/StripeWebhookHandler.java new file mode 100644 index 00000000000..bf91afd1e9e --- /dev/null +++ b/src/main/java/com/stripe/examples/StripeWebhookHandler.java @@ -0,0 +1,52 @@ +package com.stripe.examples; + +public class StripeWebhookHandler { + // private static final String API_KEY = System.getenv("STRIPE_API_KEY"); + // private static final String WEBHOOK_SECRET = System.getenv("WEBHOOK_SECRET"); + + // private static final StripeClient client = new StripeClient(API_KEY); + + // public static void main(String[] args) throws IOException { + + // HttpServer server = HttpServer.create(new InetSocketAddress(4242), 0); + // server.createContext("/webhook", new WebhookHandler()); + // server.setExecutor(null); + // server.start(); + // } + + // static class WebhookHandler implements HttpHandler { + // @Override + // public void handle(HttpExchange exchange) throws IOException { + // if ("POST".equals(exchange.getRequestMethod())) { + // InputStream requestBody = exchange.getRequestBody(); + // String webhookBody = new String(requestBody.readAllBytes(), StandardCharsets.UTF_8); + // String sigHeader = exchange.getRequestHeaders().getFirst("Stripe-Signature"); + + // try { + // ThinEvent thinEvent = client.parseThinEvent(webhookBody, sigHeader, WEBHOOK_SECRET); + + // // Fetch the event data to understand the failure + // Event baseEvent = client.v2().core().events().retrieve(thinEvent.getId()); + // if (baseEvent instanceof V1BillingMeterErrorReportTriggeredEvent) { + // V1BillingMeterErrorReportTriggeredEvent event = + // (V1BillingMeterErrorReportTriggeredEvent) baseEvent; + // Meter meter = event.fetchRelatedObject(); + + // String meterId = meter.getId(); + // System.out.println(meterId); + + // // Record the failures and alert your team + // // Add your logic here + // } + + // exchange.sendResponseHeaders(200, -1); + // } catch (StripeException e) { + // exchange.sendResponseHeaders(400, -1); + // } + // } else { + // exchange.sendResponseHeaders(405, -1); + // } + // exchange.close(); + // } + // } +} diff --git a/src/main/java/com/stripe/exception/AuthenticationException.java b/src/main/java/com/stripe/exception/AuthenticationException.java index c18a8d725bb..9a16aa2d8bc 100644 --- a/src/main/java/com/stripe/exception/AuthenticationException.java +++ b/src/main/java/com/stripe/exception/AuthenticationException.java @@ -7,4 +7,9 @@ public AuthenticationException( String message, String requestId, String code, Integer statusCode) { super(message, requestId, code, statusCode); } + + public AuthenticationException( + String message, String requestId, String code, Integer statusCode, Throwable e) { + super(message, requestId, code, statusCode, e); + } } diff --git a/src/main/java/com/stripe/exception/StripeException.java b/src/main/java/com/stripe/exception/StripeException.java index 8aeda821619..09479171da9 100644 --- a/src/main/java/com/stripe/exception/StripeException.java +++ b/src/main/java/com/stripe/exception/StripeException.java @@ -1,8 +1,9 @@ package com.stripe.exception; +import com.google.gson.JsonObject; import com.stripe.model.StripeError; +import com.stripe.net.StripeResponseGetter; import lombok.Getter; -import lombok.Setter; @Getter public abstract class StripeException extends Exception { @@ -11,7 +12,11 @@ public abstract class StripeException extends Exception { /** The error resource returned by Stripe's API that caused the exception. */ // transient so the exception can be serialized, as StripeObject does not // implement Serializable - @Setter transient StripeError stripeError; + transient StripeError stripeError; + + public void setStripeError(StripeError err) { + stripeError = err; + } /** * Returns the error code of the response that triggered this exception. For {@link ApiException} @@ -63,6 +68,9 @@ public String getMessage() { if (requestId != null) { additionalInfo += "; request-id: " + requestId; } + if (this.getUserMessage() != null) { + additionalInfo += "; user-message: " + this.getUserMessage(); + } return super.getMessage() + additionalInfo; } @@ -72,6 +80,25 @@ public String getMessage() { * @return a string representation of the user facing exception. */ public String getUserMessage() { - return super.getMessage(); + if (this.getStripeError() != null) { + return this.getStripeError().getUserMessage(); + } + return null; + } + + public static StripeException parseV2Exception( + String type, + JsonObject body, + int statusCode, + String requestId, + StripeResponseGetter responseGetter) { + switch (type) { + // The beginning of the section generated from our OpenAPI spec + case "temporary_session_expired": + return com.stripe.exception.TemporarySessionExpiredException.parse( + body, statusCode, requestId, responseGetter); + // The end of the section generated from our OpenAPI spec + } + return null; } } diff --git a/src/main/java/com/stripe/exception/TemporarySessionExpiredException.java b/src/main/java/com/stripe/exception/TemporarySessionExpiredException.java new file mode 100644 index 00000000000..376c73a885b --- /dev/null +++ b/src/main/java/com/stripe/exception/TemporarySessionExpiredException.java @@ -0,0 +1,34 @@ +// File generated from our OpenAPI spec +package com.stripe.exception; + +import com.google.gson.JsonObject; +import com.stripe.model.StripeError; +import com.stripe.model.StripeObject; +import com.stripe.net.StripeResponseGetter; + +/** The temporary session token has expired. */ +public final class TemporarySessionExpiredException extends ApiException { + private static final long serialVersionUID = 2L; + + private TemporarySessionExpiredException( + String message, String requestId, String code, Integer statusCode, Throwable e) { + super(message, requestId, code, statusCode, e); + } + + static TemporarySessionExpiredException parse( + JsonObject body, int statusCode, String requestId, StripeResponseGetter responseGetter) { + TemporarySessionExpiredException.TemporarySessionExpiredError error = + (TemporarySessionExpiredException.TemporarySessionExpiredError) + StripeObject.deserializeStripeObject( + body, + TemporarySessionExpiredException.TemporarySessionExpiredError.class, + responseGetter); + TemporarySessionExpiredException exception = + new TemporarySessionExpiredException( + error.getMessage(), requestId, error.getCode(), statusCode, null); + exception.setStripeError(error); + return exception; + } + + public static class TemporarySessionExpiredError extends StripeError {} +} diff --git a/src/main/java/com/stripe/model/Capability.java b/src/main/java/com/stripe/model/Capability.java index 3d62d9f46a8..5446816aa3d 100644 --- a/src/main/java/com/stripe/model/Capability.java +++ b/src/main/java/com/stripe/model/Capability.java @@ -60,8 +60,10 @@ public class Capability extends ApiResource implements HasId { Requirements requirements; /** - * The status of the capability. Can be {@code active}, {@code inactive}, {@code pending}, or - * {@code unrequested}. + * The status of the capability. + * + *

One of {@code active}, {@code disabled}, {@code inactive}, {@code pending}, or {@code + * unrequested}. */ @SerializedName("status") String status; diff --git a/src/main/java/com/stripe/model/CreditNote.java b/src/main/java/com/stripe/model/CreditNote.java index c1fd5c8a4ba..d9765793826 100644 --- a/src/main/java/com/stripe/model/CreditNote.java +++ b/src/main/java/com/stripe/model/CreditNote.java @@ -3,6 +3,7 @@ import com.google.gson.annotations.SerializedName; import com.stripe.exception.StripeException; +import com.stripe.model.billing.CreditBalanceTransaction; import com.stripe.net.ApiRequest; import com.stripe.net.ApiRequestParams; import com.stripe.net.ApiResource; @@ -142,6 +143,9 @@ public class CreditNote extends ApiResource implements HasId, MetadataStore pretaxCreditAmounts; + /** * Reason for issuing this credit note, one of {@code duplicate}, {@code fraudulent}, {@code * order_change}, or {@code product_unsatisfactory}. @@ -647,6 +651,75 @@ public void setDiscountObject(Discount expandableObject) { } } + @Getter + @Setter + @EqualsAndHashCode(callSuper = false) + public static class PretaxCreditAmount extends StripeObject { + /** The amount, in cents (or local equivalent), of the pretax credit amount. */ + @SerializedName("amount") + Long amount; + + /** The credit balance transaction that was applied to get this pretax credit amount. */ + @SerializedName("credit_balance_transaction") + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + ExpandableField creditBalanceTransaction; + + /** The discount that was applied to get this pretax credit amount. */ + @SerializedName("discount") + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + ExpandableField discount; + + /** + * Type of the pretax credit amount referenced. + * + *

One of {@code credit_balance_transaction}, or {@code discount}. + */ + @SerializedName("type") + String type; + + /** Get ID of expandable {@code creditBalanceTransaction} object. */ + public String getCreditBalanceTransaction() { + return (this.creditBalanceTransaction != null) ? this.creditBalanceTransaction.getId() : null; + } + + public void setCreditBalanceTransaction(String id) { + this.creditBalanceTransaction = + ApiResource.setExpandableFieldId(id, this.creditBalanceTransaction); + } + + /** Get expanded {@code creditBalanceTransaction}. */ + public CreditBalanceTransaction getCreditBalanceTransactionObject() { + return (this.creditBalanceTransaction != null) + ? this.creditBalanceTransaction.getExpanded() + : null; + } + + public void setCreditBalanceTransactionObject(CreditBalanceTransaction expandableObject) { + this.creditBalanceTransaction = + new ExpandableField(expandableObject.getId(), expandableObject); + } + + /** Get ID of expandable {@code discount} object. */ + public String getDiscount() { + return (this.discount != null) ? this.discount.getId() : null; + } + + public void setDiscount(String id) { + this.discount = ApiResource.setExpandableFieldId(id, this.discount); + } + + /** Get expanded {@code discount}. */ + public Discount getDiscountObject() { + return (this.discount != null) ? this.discount.getExpanded() : null; + } + + public void setDiscountObject(Discount expandableObject) { + this.discount = new ExpandableField(expandableObject.getId(), expandableObject); + } + } + @Getter @Setter @EqualsAndHashCode(callSuper = false) diff --git a/src/main/java/com/stripe/model/CreditNoteLineItem.java b/src/main/java/com/stripe/model/CreditNoteLineItem.java index 78b0f2f3f57..e22dec665c7 100644 --- a/src/main/java/com/stripe/model/CreditNoteLineItem.java +++ b/src/main/java/com/stripe/model/CreditNoteLineItem.java @@ -2,6 +2,7 @@ package com.stripe.model; import com.google.gson.annotations.SerializedName; +import com.stripe.model.billing.CreditBalanceTransaction; import com.stripe.net.ApiResource; import java.math.BigDecimal; import java.util.List; @@ -67,6 +68,9 @@ public class CreditNoteLineItem extends StripeObject implements HasId { @SerializedName("object") String object; + @SerializedName("pretax_credit_amounts") + List pretaxCreditAmounts; + /** The number of units of product being credited. */ @SerializedName("quantity") Long quantity; @@ -136,6 +140,75 @@ public void setDiscountObject(Discount expandableObject) { } } + @Getter + @Setter + @EqualsAndHashCode(callSuper = false) + public static class PretaxCreditAmount extends StripeObject { + /** The amount, in cents (or local equivalent), of the pretax credit amount. */ + @SerializedName("amount") + Long amount; + + /** The credit balance transaction that was applied to get this pretax credit amount. */ + @SerializedName("credit_balance_transaction") + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + ExpandableField creditBalanceTransaction; + + /** The discount that was applied to get this pretax credit amount. */ + @SerializedName("discount") + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + ExpandableField discount; + + /** + * Type of the pretax credit amount referenced. + * + *

One of {@code credit_balance_transaction}, or {@code discount}. + */ + @SerializedName("type") + String type; + + /** Get ID of expandable {@code creditBalanceTransaction} object. */ + public String getCreditBalanceTransaction() { + return (this.creditBalanceTransaction != null) ? this.creditBalanceTransaction.getId() : null; + } + + public void setCreditBalanceTransaction(String id) { + this.creditBalanceTransaction = + ApiResource.setExpandableFieldId(id, this.creditBalanceTransaction); + } + + /** Get expanded {@code creditBalanceTransaction}. */ + public CreditBalanceTransaction getCreditBalanceTransactionObject() { + return (this.creditBalanceTransaction != null) + ? this.creditBalanceTransaction.getExpanded() + : null; + } + + public void setCreditBalanceTransactionObject(CreditBalanceTransaction expandableObject) { + this.creditBalanceTransaction = + new ExpandableField(expandableObject.getId(), expandableObject); + } + + /** Get ID of expandable {@code discount} object. */ + public String getDiscount() { + return (this.discount != null) ? this.discount.getId() : null; + } + + public void setDiscount(String id) { + this.discount = ApiResource.setExpandableFieldId(id, this.discount); + } + + /** Get expanded {@code discount}. */ + public Discount getDiscountObject() { + return (this.discount != null) ? this.discount.getExpanded() : null; + } + + public void setDiscountObject(Discount expandableObject) { + this.discount = new ExpandableField(expandableObject.getId(), expandableObject); + } + } + @Getter @Setter @EqualsAndHashCode(callSuper = false) diff --git a/src/main/java/com/stripe/model/Customer.java b/src/main/java/com/stripe/model/Customer.java index ee6edc8a467..aee848a4c6e 100644 --- a/src/main/java/com/stripe/model/Customer.java +++ b/src/main/java/com/stripe/model/Customer.java @@ -28,11 +28,10 @@ import lombok.Setter; /** - * This object represents a customer of your business. Use it to create recurring charges and track - * payments that belong to the same customer. - * - *

Related guide: Save a card - * during payment + * This object represents a customer of your business. Use it to create recurring charges, save payment and contact + * information, and track payments that belong to the same customer. */ @Getter @Setter diff --git a/src/main/java/com/stripe/model/Event.java b/src/main/java/com/stripe/model/Event.java index b32ffd1a1fe..f4f32f96fa2 100644 --- a/src/main/java/com/stripe/model/Event.java +++ b/src/main/java/com/stripe/model/Event.java @@ -5,6 +5,7 @@ import com.google.gson.JsonParseException; import com.google.gson.annotations.SerializedName; import com.stripe.exception.StripeException; +import com.stripe.net.ApiMode; import com.stripe.net.ApiRequest; import com.stripe.net.ApiRequestParams; import com.stripe.net.ApiResource; @@ -363,7 +364,7 @@ public static class Data extends StripeObject implements StripeActiveObject { */ @Deprecated public StripeObject getObject() { - return StripeObject.deserializeStripeObject(object, this.responseGetter); + return StripeObject.deserializeStripeObject(object, this.responseGetter, ApiMode.V1); } @Override diff --git a/src/main/java/com/stripe/model/EventDataClassLookup.java b/src/main/java/com/stripe/model/EventDataClassLookup.java index ef93e6eb9c5..33d9775c0f3 100644 --- a/src/main/java/com/stripe/model/EventDataClassLookup.java +++ b/src/main/java/com/stripe/model/EventDataClassLookup.java @@ -7,8 +7,10 @@ /** * Event data class look up used in event deserialization. The key to look up is `object` string of * the model. + * + *

For internal use by Stripe SDK only. */ -final class EventDataClassLookup { +public final class EventDataClassLookup { public static final Map> classLookup = new HashMap<>(); static { @@ -51,6 +53,7 @@ final class EventDataClassLookup { classLookup.put("line_item", InvoiceLineItem.class); classLookup.put("login_link", LoginLink.class); classLookup.put("mandate", Mandate.class); + classLookup.put("margin", Margin.class); classLookup.put("payment_intent", PaymentIntent.class); classLookup.put("payment_link", PaymentLink.class); classLookup.put("payment_method", PaymentMethod.class); @@ -92,6 +95,12 @@ final class EventDataClassLookup { classLookup.put("billing.alert", com.stripe.model.billing.Alert.class); classLookup.put("billing.alert_triggered", com.stripe.model.billing.AlertTriggered.class); + classLookup.put( + "billing.credit_balance_summary", com.stripe.model.billing.CreditBalanceSummary.class); + classLookup.put( + "billing.credit_balance_transaction", + com.stripe.model.billing.CreditBalanceTransaction.class); + classLookup.put("billing.credit_grant", com.stripe.model.billing.CreditGrant.class); classLookup.put("billing.meter", com.stripe.model.billing.Meter.class); classLookup.put("billing.meter_event", com.stripe.model.billing.MeterEvent.class); classLookup.put( diff --git a/src/main/java/com/stripe/model/EventDataObjectDeserializer.java b/src/main/java/com/stripe/model/EventDataObjectDeserializer.java index 3ef40acd453..2cdef7bd9d2 100644 --- a/src/main/java/com/stripe/model/EventDataObjectDeserializer.java +++ b/src/main/java/com/stripe/model/EventDataObjectDeserializer.java @@ -4,6 +4,7 @@ import com.google.gson.JsonParseException; import com.stripe.Stripe; import com.stripe.exception.EventDataObjectDeserializationException; +import com.stripe.net.ApiMode; import com.stripe.net.StripeResponseGetter; import java.util.Map; import java.util.Optional; @@ -121,7 +122,8 @@ private boolean deserialize() { return true; } else { try { - object = StripeObject.deserializeStripeObject(rawJsonObject, this.responseGetter); + object = + StripeObject.deserializeStripeObject(rawJsonObject, this.responseGetter, ApiMode.V1); return true; } catch (JsonParseException e) { // intentionally ignore exception to fulfill simply whether deserialization succeeds @@ -146,7 +148,7 @@ private boolean deserialize() { */ public StripeObject deserializeUnsafe() throws EventDataObjectDeserializationException { try { - return StripeObject.deserializeStripeObject(rawJsonObject, this.responseGetter); + return StripeObject.deserializeStripeObject(rawJsonObject, this.responseGetter, ApiMode.V1); } catch (JsonParseException e) { String errorMessage; if (!apiVersionMatch()) { @@ -190,7 +192,8 @@ public StripeObject deserializeUnsafe() throws EventDataObjectDeserializationExc public StripeObject deserializeUnsafeWith(CompatibilityTransformer transformer) { return StripeObject.deserializeStripeObject( transformer.transform(rawJsonObject.deepCopy(), apiVersion, eventType), - this.responseGetter); + this.responseGetter, + ApiMode.V1); } private boolean apiVersionMatch() { diff --git a/src/main/java/com/stripe/model/InstantDeserializer.java b/src/main/java/com/stripe/model/InstantDeserializer.java new file mode 100644 index 00000000000..64447cc1f4a --- /dev/null +++ b/src/main/java/com/stripe/model/InstantDeserializer.java @@ -0,0 +1,32 @@ +package com.stripe.model; + +import com.google.gson.*; +import java.lang.reflect.Type; +import java.time.Instant; + +public class InstantDeserializer implements JsonDeserializer, JsonSerializer { + /** Deserializes an timestamp JSON payload into an {@link java.time.Instant} object. */ + @Override + public Instant deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + if (json.isJsonNull()) { + return null; + } + + if (json.isJsonPrimitive()) { + JsonPrimitive jsonPrimitive = json.getAsJsonPrimitive(); + if (jsonPrimitive.isString()) { + return Instant.parse(jsonPrimitive.getAsString()); + } + + throw new JsonParseException("Instant is a non-string primitive type."); + } + + throw new JsonParseException("Instant is a non-primitive type."); + } + + @Override + public JsonElement serialize(Instant src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(src.toString()); + } +} diff --git a/src/main/java/com/stripe/model/InstantSerializer.java b/src/main/java/com/stripe/model/InstantSerializer.java new file mode 100644 index 00000000000..580fb234961 --- /dev/null +++ b/src/main/java/com/stripe/model/InstantSerializer.java @@ -0,0 +1,19 @@ +package com.stripe.model; + +import com.google.gson.JsonElement; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import java.lang.reflect.Type; +import java.time.Instant; + +public class InstantSerializer implements JsonSerializer { + /** Serializes an Instant into a JSON string in ISO 8601 format. */ + @Override + public JsonElement serialize(Instant src, Type typeOfSrc, JsonSerializationContext context) { + if (src != null) { + return new JsonPrimitive(src.toString()); + } + return null; + } +} diff --git a/src/main/java/com/stripe/model/Invoice.java b/src/main/java/com/stripe/model/Invoice.java index 1e95ff9de66..157984478a2 100644 --- a/src/main/java/com/stripe/model/Invoice.java +++ b/src/main/java/com/stripe/model/Invoice.java @@ -3,6 +3,7 @@ import com.google.gson.annotations.SerializedName; import com.stripe.exception.StripeException; +import com.stripe.model.billing.CreditBalanceTransaction; import com.stripe.model.testhelpers.TestClock; import com.stripe.net.ApiRequest; import com.stripe.net.ApiRequestParams; @@ -615,6 +616,9 @@ public class Invoice extends ApiResource implements HasId, MetadataStore totalPretaxCreditAmounts; + /** The aggregate amounts calculated per tax rate for all line items. */ @SerializedName("total_tax_amounts") List totalTaxAmounts; @@ -2801,6 +2805,99 @@ public void setDiscountObject(Discount expandableObject) { } } + @Getter + @Setter + @EqualsAndHashCode(callSuper = false) + public static class TotalPretaxCreditAmount extends StripeObject { + /** The amount, in cents (or local equivalent), of the pretax credit amount. */ + @SerializedName("amount") + Long amount; + + /** The credit balance transaction that was applied to get this pretax credit amount. */ + @SerializedName("credit_balance_transaction") + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + ExpandableField creditBalanceTransaction; + + /** The discount that was applied to get this pretax credit amount. */ + @SerializedName("discount") + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + ExpandableField discount; + + /** The margin that was applied to get this pretax credit amount. */ + @SerializedName("margin") + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + ExpandableField margin; + + /** + * Type of the pretax credit amount referenced. + * + *

One of {@code credit_balance_transaction}, or {@code discount}. + */ + @SerializedName("type") + String type; + + /** Get ID of expandable {@code creditBalanceTransaction} object. */ + public String getCreditBalanceTransaction() { + return (this.creditBalanceTransaction != null) ? this.creditBalanceTransaction.getId() : null; + } + + public void setCreditBalanceTransaction(String id) { + this.creditBalanceTransaction = + ApiResource.setExpandableFieldId(id, this.creditBalanceTransaction); + } + + /** Get expanded {@code creditBalanceTransaction}. */ + public CreditBalanceTransaction getCreditBalanceTransactionObject() { + return (this.creditBalanceTransaction != null) + ? this.creditBalanceTransaction.getExpanded() + : null; + } + + public void setCreditBalanceTransactionObject(CreditBalanceTransaction expandableObject) { + this.creditBalanceTransaction = + new ExpandableField(expandableObject.getId(), expandableObject); + } + + /** Get ID of expandable {@code discount} object. */ + public String getDiscount() { + return (this.discount != null) ? this.discount.getId() : null; + } + + public void setDiscount(String id) { + this.discount = ApiResource.setExpandableFieldId(id, this.discount); + } + + /** Get expanded {@code discount}. */ + public Discount getDiscountObject() { + return (this.discount != null) ? this.discount.getExpanded() : null; + } + + public void setDiscountObject(Discount expandableObject) { + this.discount = new ExpandableField(expandableObject.getId(), expandableObject); + } + + /** Get ID of expandable {@code margin} object. */ + public String getMargin() { + return (this.margin != null) ? this.margin.getId() : null; + } + + public void setMargin(String id) { + this.margin = ApiResource.setExpandableFieldId(id, this.margin); + } + + /** Get expanded {@code margin}. */ + public Margin getMarginObject() { + return (this.margin != null) ? this.margin.getExpanded() : null; + } + + public void setMarginObject(Margin expandableObject) { + this.margin = new ExpandableField(expandableObject.getId(), expandableObject); + } + } + @Getter @Setter @EqualsAndHashCode(callSuper = false) diff --git a/src/main/java/com/stripe/model/InvoiceLineItem.java b/src/main/java/com/stripe/model/InvoiceLineItem.java index 40fe6c4477e..cca366a045a 100644 --- a/src/main/java/com/stripe/model/InvoiceLineItem.java +++ b/src/main/java/com/stripe/model/InvoiceLineItem.java @@ -3,6 +3,7 @@ import com.google.gson.annotations.SerializedName; import com.stripe.exception.StripeException; +import com.stripe.model.billing.CreditBalanceTransaction; import com.stripe.net.ApiRequest; import com.stripe.net.ApiRequestParams; import com.stripe.net.ApiResource; @@ -119,6 +120,9 @@ public class InvoiceLineItem extends ApiResource implements HasId, MetadataStore @SerializedName("plan") Plan plan; + @SerializedName("pretax_credit_amounts") + List pretaxCreditAmounts; + /** The price of the line item. */ @SerializedName("price") Price price; @@ -386,6 +390,99 @@ public static class Period extends StripeObject { Long start; } + @Getter + @Setter + @EqualsAndHashCode(callSuper = false) + public static class PretaxCreditAmount extends StripeObject { + /** The amount, in cents (or local equivalent), of the pretax credit amount. */ + @SerializedName("amount") + Long amount; + + /** The credit balance transaction that was applied to get this pretax credit amount. */ + @SerializedName("credit_balance_transaction") + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + ExpandableField creditBalanceTransaction; + + /** The discount that was applied to get this pretax credit amount. */ + @SerializedName("discount") + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + ExpandableField discount; + + /** The margin that was applied to get this pretax credit amount. */ + @SerializedName("margin") + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + ExpandableField margin; + + /** + * Type of the pretax credit amount referenced. + * + *

One of {@code credit_balance_transaction}, or {@code discount}. + */ + @SerializedName("type") + String type; + + /** Get ID of expandable {@code creditBalanceTransaction} object. */ + public String getCreditBalanceTransaction() { + return (this.creditBalanceTransaction != null) ? this.creditBalanceTransaction.getId() : null; + } + + public void setCreditBalanceTransaction(String id) { + this.creditBalanceTransaction = + ApiResource.setExpandableFieldId(id, this.creditBalanceTransaction); + } + + /** Get expanded {@code creditBalanceTransaction}. */ + public CreditBalanceTransaction getCreditBalanceTransactionObject() { + return (this.creditBalanceTransaction != null) + ? this.creditBalanceTransaction.getExpanded() + : null; + } + + public void setCreditBalanceTransactionObject(CreditBalanceTransaction expandableObject) { + this.creditBalanceTransaction = + new ExpandableField(expandableObject.getId(), expandableObject); + } + + /** Get ID of expandable {@code discount} object. */ + public String getDiscount() { + return (this.discount != null) ? this.discount.getId() : null; + } + + public void setDiscount(String id) { + this.discount = ApiResource.setExpandableFieldId(id, this.discount); + } + + /** Get expanded {@code discount}. */ + public Discount getDiscountObject() { + return (this.discount != null) ? this.discount.getExpanded() : null; + } + + public void setDiscountObject(Discount expandableObject) { + this.discount = new ExpandableField(expandableObject.getId(), expandableObject); + } + + /** Get ID of expandable {@code margin} object. */ + public String getMargin() { + return (this.margin != null) ? this.margin.getId() : null; + } + + public void setMargin(String id) { + this.margin = ApiResource.setExpandableFieldId(id, this.margin); + } + + /** Get expanded {@code margin}. */ + public Margin getMarginObject() { + return (this.margin != null) ? this.margin.getExpanded() : null; + } + + public void setMarginObject(Margin expandableObject) { + this.margin = new ExpandableField(expandableObject.getId(), expandableObject); + } + } + @Getter @Setter @EqualsAndHashCode(callSuper = false) diff --git a/src/main/java/com/stripe/model/Margin.java b/src/main/java/com/stripe/model/Margin.java new file mode 100644 index 00000000000..c07035c635e --- /dev/null +++ b/src/main/java/com/stripe/model/Margin.java @@ -0,0 +1,72 @@ +// File generated from our OpenAPI spec +package com.stripe.model; + +import com.google.gson.annotations.SerializedName; +import java.math.BigDecimal; +import java.util.Map; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * A (partner) margin represents a specific discount distributed in partner reseller programs to + * business partners who resell products and services and earn a discount (margin) for doing so. + */ +@Getter +@Setter +@EqualsAndHashCode(callSuper = false) +public class Margin extends StripeObject implements HasId { + /** + * Whether the margin can be applied to invoices, invoice items, or invoice line items. Defaults + * to {@code true}. + */ + @SerializedName("active") + Boolean active; + + /** Time at which the object was created. Measured in seconds since the Unix epoch. */ + @SerializedName("created") + Long created; + + /** Unique identifier for the object. */ + @Getter(onMethod_ = {@Override}) + @SerializedName("id") + String id; + + /** + * Has the value {@code true} if the object exists in live mode or the value {@code false} if the + * object exists in test mode. + */ + @SerializedName("livemode") + Boolean livemode; + + /** + * Set of key-value pairs that you can attach + * to an object. This can be useful for storing additional information about the object in a + * structured format. + */ + @SerializedName("metadata") + Map metadata; + + /** Name of the margin that's displayed on, for example, invoices. */ + @SerializedName("name") + String name; + + /** + * String representing the object's type. Objects of the same type share the same value. + * + *

Equal to {@code margin}. + */ + @SerializedName("object") + String object; + + /** + * Percent that will be taken off the subtotal before tax (after all other discounts and + * promotions) of any invoice to which the margin is applied. + */ + @SerializedName("percent_off") + BigDecimal percentOff; + + /** Time at which the object was last updated. Measured in seconds since the Unix epoch. */ + @SerializedName("updated") + Long updated; +} diff --git a/src/main/java/com/stripe/model/PromotionCode.java b/src/main/java/com/stripe/model/PromotionCode.java index 06079e876e8..565bf323612 100644 --- a/src/main/java/com/stripe/model/PromotionCode.java +++ b/src/main/java/com/stripe/model/PromotionCode.java @@ -36,7 +36,8 @@ public class PromotionCode extends ApiResource implements HasId, MetadataStore

extends StripeObject implements StripeCollectionInterface, StripeActiveObject { private transient StripeResponseGetter responseGetter; + String object; @Getter(onMethod_ = {@Override}) @@ -69,7 +69,6 @@ public Iterable autoPagingIterable() { public Iterable autoPagingIterable(Map params) { this.responseGetter.validateRequestOptions(this.requestOptions); - this.setRequestParams(params); return new PagingIterable<>(this, responseGetter, pageTypeToken); } @@ -92,6 +91,7 @@ public Iterable autoPagingIterable(Map params, RequestOptions @Override public void setResponseGetter(StripeResponseGetter responseGetter) { this.responseGetter = responseGetter; + if (this.data != null) { for (T item : data) { trySetResponseGetter(item, responseGetter); diff --git a/src/main/java/com/stripe/model/StripeError.java b/src/main/java/com/stripe/model/StripeError.java index ac6c8425fc4..2382a2293c3 100644 --- a/src/main/java/com/stripe/model/StripeError.java +++ b/src/main/java/com/stripe/model/StripeError.java @@ -213,4 +213,8 @@ public class StripeError extends StripeObject { */ @SerializedName("type") String type; + + /** The user message associated with the error. */ + @SerializedName("user_message") + String userMessage; } diff --git a/src/main/java/com/stripe/model/StripeObject.java b/src/main/java/com/stripe/model/StripeObject.java index 825370c6045..68e4e7ed059 100644 --- a/src/main/java/com/stripe/model/StripeObject.java +++ b/src/main/java/com/stripe/model/StripeObject.java @@ -4,11 +4,13 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; +import com.stripe.net.ApiMode; import com.stripe.net.ApiResource; import com.stripe.net.StripeResponse; import com.stripe.net.StripeResponseGetter; import java.lang.reflect.Field; import java.lang.reflect.Type; +import java.time.Instant; public abstract class StripeObject implements StripeObjectInterface { public static final Gson PRETTY_PRINT_GSON = @@ -17,6 +19,7 @@ public abstract class StripeObject implements StripeObjectInterface { .serializeNulls() .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) .registerTypeAdapter(ExpandableField.class, new ExpandableFieldSerializer()) + .registerTypeAdapter(Instant.class, new InstantSerializer()) .create(); private transient StripeResponse lastResponse; @@ -97,13 +100,20 @@ protected static boolean equals(Object a, Object b) { * @return JSON data to be deserialized to super class {@code StripeObject} */ static StripeObject deserializeStripeObject( - JsonObject eventDataObjectJson, StripeResponseGetter responseGetter) { + JsonObject eventDataObjectJson, StripeResponseGetter responseGetter, ApiMode apiMode) { String type = eventDataObjectJson.getAsJsonObject().get("object").getAsString(); - Class cl = EventDataClassLookup.classLookup.get(type); - StripeObject object = - ApiResource.deserializeStripeObject( - eventDataObjectJson, cl != null ? cl : StripeRawJsonObject.class, responseGetter); - return object; + Class cl = + (apiMode == ApiMode.V1) + ? com.stripe.model.EventDataClassLookup.classLookup.get(type) + : com.stripe.model.v2.EventDataClassLookup.classLookup.get(type); + return StripeObject.deserializeStripeObject( + eventDataObjectJson, cl != null ? cl : StripeRawJsonObject.class, responseGetter); + } + + public static StripeObject deserializeStripeObject( + String payload, StripeResponseGetter responseGetter, ApiMode apiMode) { + JsonObject jsonObject = ApiResource.GSON.fromJson(payload, JsonObject.class).getAsJsonObject(); + return deserializeStripeObject(jsonObject, responseGetter, apiMode); } public static StripeObject deserializeStripeObject( diff --git a/src/main/java/com/stripe/model/StripeSearchResult.java b/src/main/java/com/stripe/model/StripeSearchResult.java index 94192de7d9b..d843e6b5b7d 100644 --- a/src/main/java/com/stripe/model/StripeSearchResult.java +++ b/src/main/java/com/stripe/model/StripeSearchResult.java @@ -15,7 +15,9 @@ @EqualsAndHashCode(callSuper = false) public class StripeSearchResult extends StripeObject implements StripeSearchResultInterface, StripeActiveObject { + private transient StripeResponseGetter responseGetter; + String object; @Getter(onMethod_ = {@Override}) diff --git a/src/main/java/com/stripe/model/Subscription.java b/src/main/java/com/stripe/model/Subscription.java index 57c31baff91..3d0f489a9e2 100644 --- a/src/main/java/com/stripe/model/Subscription.java +++ b/src/main/java/com/stripe/model/Subscription.java @@ -590,17 +590,17 @@ public void setDiscountObjects(List objs) { } /** - * Cancels a customer’s subscription immediately. The customer will not be charged again for the - * subscription. + * Cancels a customer’s subscription immediately. The customer won’t be charged again for the + * subscription. After it’s canceled, you can no longer update the subscription or its metadata. * - *

Note, however, that any pending invoice items that you’ve created will still be charged for - * at the end of the period, unless manually deleted. If you’ve set the - * subscription to cancel at the end of the period, any pending prorations will also be left in - * place and collected at the end of the period. But if the subscription is set to cancel - * immediately, pending prorations will be removed. + *

Any pending invoice items that you’ve created are still charged at the end of the period, + * unless manually deleted. If you’ve + * set the subscription to cancel at the end of the period, any pending prorations are also left + * in place and collected at the end of the period. But if the subscription is set to cancel + * immediately, pending prorations are removed. * - *

By default, upon subscription cancellation, Stripe will stop automatic collection of all + *

By default, upon subscription cancellation, Stripe stops automatic collection of all * finalized invoices for the customer. This is intended to prevent unexpected payment attempts * after the customer has canceled a subscription. However, you can resume automatic collection of * the invoices manually after subscription cancellation to have us proceed. Or, you could check @@ -611,17 +611,17 @@ public Subscription cancel() throws StripeException { } /** - * Cancels a customer’s subscription immediately. The customer will not be charged again for the - * subscription. + * Cancels a customer’s subscription immediately. The customer won’t be charged again for the + * subscription. After it’s canceled, you can no longer update the subscription or its metadata. * - *

Note, however, that any pending invoice items that you’ve created will still be charged for - * at the end of the period, unless manually deleted. If you’ve set the - * subscription to cancel at the end of the period, any pending prorations will also be left in - * place and collected at the end of the period. But if the subscription is set to cancel - * immediately, pending prorations will be removed. + *

Any pending invoice items that you’ve created are still charged at the end of the period, + * unless manually deleted. If you’ve + * set the subscription to cancel at the end of the period, any pending prorations are also left + * in place and collected at the end of the period. But if the subscription is set to cancel + * immediately, pending prorations are removed. * - *

By default, upon subscription cancellation, Stripe will stop automatic collection of all + *

By default, upon subscription cancellation, Stripe stops automatic collection of all * finalized invoices for the customer. This is intended to prevent unexpected payment attempts * after the customer has canceled a subscription. However, you can resume automatic collection of * the invoices manually after subscription cancellation to have us proceed. Or, you could check @@ -632,17 +632,17 @@ public Subscription cancel(Map params) throws StripeException { } /** - * Cancels a customer’s subscription immediately. The customer will not be charged again for the - * subscription. + * Cancels a customer’s subscription immediately. The customer won’t be charged again for the + * subscription. After it’s canceled, you can no longer update the subscription or its metadata. * - *

Note, however, that any pending invoice items that you’ve created will still be charged for - * at the end of the period, unless manually deleted. If you’ve set the - * subscription to cancel at the end of the period, any pending prorations will also be left in - * place and collected at the end of the period. But if the subscription is set to cancel - * immediately, pending prorations will be removed. + *

Any pending invoice items that you’ve created are still charged at the end of the period, + * unless manually deleted. If you’ve + * set the subscription to cancel at the end of the period, any pending prorations are also left + * in place and collected at the end of the period. But if the subscription is set to cancel + * immediately, pending prorations are removed. * - *

By default, upon subscription cancellation, Stripe will stop automatic collection of all + *

By default, upon subscription cancellation, Stripe stops automatic collection of all * finalized invoices for the customer. This is intended to prevent unexpected payment attempts * after the customer has canceled a subscription. However, you can resume automatic collection of * the invoices manually after subscription cancellation to have us proceed. Or, you could check @@ -657,17 +657,17 @@ public Subscription cancel(Map params, RequestOptions options) } /** - * Cancels a customer’s subscription immediately. The customer will not be charged again for the - * subscription. + * Cancels a customer’s subscription immediately. The customer won’t be charged again for the + * subscription. After it’s canceled, you can no longer update the subscription or its metadata. * - *

Note, however, that any pending invoice items that you’ve created will still be charged for - * at the end of the period, unless manually deleted. If you’ve set the - * subscription to cancel at the end of the period, any pending prorations will also be left in - * place and collected at the end of the period. But if the subscription is set to cancel - * immediately, pending prorations will be removed. + *

Any pending invoice items that you’ve created are still charged at the end of the period, + * unless manually deleted. If you’ve + * set the subscription to cancel at the end of the period, any pending prorations are also left + * in place and collected at the end of the period. But if the subscription is set to cancel + * immediately, pending prorations are removed. * - *

By default, upon subscription cancellation, Stripe will stop automatic collection of all + *

By default, upon subscription cancellation, Stripe stops automatic collection of all * finalized invoices for the customer. This is intended to prevent unexpected payment attempts * after the customer has canceled a subscription. However, you can resume automatic collection of * the invoices manually after subscription cancellation to have us proceed. Or, you could check @@ -678,17 +678,17 @@ public Subscription cancel(SubscriptionCancelParams params) throws StripeExcepti } /** - * Cancels a customer’s subscription immediately. The customer will not be charged again for the - * subscription. + * Cancels a customer’s subscription immediately. The customer won’t be charged again for the + * subscription. After it’s canceled, you can no longer update the subscription or its metadata. * - *

Note, however, that any pending invoice items that you’ve created will still be charged for - * at the end of the period, unless manually deleted. If you’ve set the - * subscription to cancel at the end of the period, any pending prorations will also be left in - * place and collected at the end of the period. But if the subscription is set to cancel - * immediately, pending prorations will be removed. + *

Any pending invoice items that you’ve created are still charged at the end of the period, + * unless manually deleted. If you’ve + * set the subscription to cancel at the end of the period, any pending prorations are also left + * in place and collected at the end of the period. But if the subscription is set to cancel + * immediately, pending prorations are removed. * - *

By default, upon subscription cancellation, Stripe will stop automatic collection of all + *

By default, upon subscription cancellation, Stripe stops automatic collection of all * finalized invoices for the customer. This is intended to prevent unexpected payment attempts * after the customer has canceled a subscription. However, you can resume automatic collection of * the invoices manually after subscription cancellation to have us proceed. Or, you could check diff --git a/src/main/java/com/stripe/model/TODO.java b/src/main/java/com/stripe/model/TODO.java new file mode 100644 index 00000000000..8c4437f1618 --- /dev/null +++ b/src/main/java/com/stripe/model/TODO.java @@ -0,0 +1,7 @@ +package com.stripe.model; + +/** + * Represents a type that cannot properly be generated, due to a problem with the generator or a + * non-standard API definition. + */ +public class TODO extends StripeObject {} diff --git a/src/main/java/com/stripe/model/ThinEvent.java b/src/main/java/com/stripe/model/ThinEvent.java new file mode 100644 index 00000000000..aa989cfa403 --- /dev/null +++ b/src/main/java/com/stripe/model/ThinEvent.java @@ -0,0 +1,34 @@ +package com.stripe.model; + +import com.google.gson.annotations.SerializedName; +import java.time.Instant; +import lombok.Getter; + +/** + * ThinEvent represents the json that's delivered from an Event Destination. It's a basic class with + * no additional methods or properties. Use it to check basic information about a delivered event. + * If you want more details, use `stripeClient.v2().core().events().retrieve(thin_event.id)` to + * fetch the full event object. + */ +@Getter +public class ThinEvent { + /** Unique identifier for the event. */ + @SerializedName("id") + public String id; + + /** The type of the event. */ + @SerializedName("type") + public String type; + + /** Time at which the object was created. */ + @SerializedName("created") + public Instant created; + + /** [Optional] Authentication context needed to fetch the event or related object. */ + @SerializedName("context") + public String context; + + /** [Optional] Object containing the reference to API resource relevant to the event. */ + @SerializedName("related_object") + public ThinEventRelatedObject relatedObject; +} diff --git a/src/main/java/com/stripe/model/ThinEventRelatedObject.java b/src/main/java/com/stripe/model/ThinEventRelatedObject.java new file mode 100644 index 00000000000..48ea526eca3 --- /dev/null +++ b/src/main/java/com/stripe/model/ThinEventRelatedObject.java @@ -0,0 +1,16 @@ +package com.stripe.model; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; + +@Getter +public class ThinEventRelatedObject { + @SerializedName("id") + public String id; + + @SerializedName("type") + public String type; + + @SerializedName("url") + public String url; +} diff --git a/src/main/java/com/stripe/model/billing/Alert.java b/src/main/java/com/stripe/model/billing/Alert.java index db248973a72..d8c7f04fdb5 100644 --- a/src/main/java/com/stripe/model/billing/Alert.java +++ b/src/main/java/com/stripe/model/billing/Alert.java @@ -19,6 +19,7 @@ import com.stripe.param.billing.AlertDeactivateParams; import com.stripe.param.billing.AlertListParams; import com.stripe.param.billing.AlertRetrieveParams; +import java.util.List; import java.util.Map; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -41,13 +42,6 @@ public class Alert extends ApiResource implements HasId { @SerializedName("alert_type") String alertType; - /** - * Limits the scope of the alert to a specific customer. - */ - @SerializedName("filter") - Filter filter; - /** Unique identifier for the object. */ @Getter(onMethod_ = {@Override}) @SerializedName("id") @@ -84,8 +78,8 @@ public class Alert extends ApiResource implements HasId { * Encapsulates configuration of the alert to monitor usage on a specific Billing Meter. */ - @SerializedName("usage_threshold_config") - UsageThresholdConfig usageThresholdConfig; + @SerializedName("usage_threshold") + UsageThreshold usageThreshold; /** Reactivates this alert, allowing it to trigger again. */ public Alert activate() throws StripeException { @@ -323,35 +317,6 @@ public static Alert retrieve(String id, AlertRetrieveParams params, RequestOptio return getGlobalResponseGetter().request(request, Alert.class); } - @Getter - @Setter - @EqualsAndHashCode(callSuper = false) - public static class Filter extends StripeObject { - /** Limit the scope of the alert to this customer ID. */ - @SerializedName("customer") - @Getter(lombok.AccessLevel.NONE) - @Setter(lombok.AccessLevel.NONE) - ExpandableField customer; - - /** Get ID of expandable {@code customer} object. */ - public String getCustomer() { - return (this.customer != null) ? this.customer.getId() : null; - } - - public void setCustomer(String id) { - this.customer = ApiResource.setExpandableFieldId(id, this.customer); - } - - /** Get expanded {@code customer}. */ - public Customer getCustomerObject() { - return (this.customer != null) ? this.customer.getExpanded() : null; - } - - public void setCustomerObject(Customer expandableObject) { - this.customer = new ExpandableField(expandableObject.getId(), expandableObject); - } - } - /** * The usage threshold alert configuration enables setting up alerts for when a certain usage * threshold on a specific meter is crossed. @@ -359,7 +324,14 @@ public void setCustomerObject(Customer expandableObject) { @Getter @Setter @EqualsAndHashCode(callSuper = false) - public static class UsageThresholdConfig extends StripeObject { + public static class UsageThreshold extends StripeObject { + /** + * The filters allow limiting the scope of this usage alert. You can only specify up to one + * filter at this time. + */ + @SerializedName("filters") + List filters; + /** The value at which this alert will trigger. */ @SerializedName("gte") Long gte; @@ -398,12 +370,43 @@ public Meter getMeterObject() { public void setMeterObject(Meter expandableObject) { this.meter = new ExpandableField(expandableObject.getId(), expandableObject); } + + @Getter + @Setter + @EqualsAndHashCode(callSuper = false) + public static class Filter extends StripeObject { + /** Limit the scope of the alert to this customer ID. */ + @SerializedName("customer") + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + ExpandableField customer; + + @SerializedName("type") + String type; + + /** Get ID of expandable {@code customer} object. */ + public String getCustomer() { + return (this.customer != null) ? this.customer.getId() : null; + } + + public void setCustomer(String id) { + this.customer = ApiResource.setExpandableFieldId(id, this.customer); + } + + /** Get expanded {@code customer}. */ + public Customer getCustomerObject() { + return (this.customer != null) ? this.customer.getExpanded() : null; + } + + public void setCustomerObject(Customer expandableObject) { + this.customer = new ExpandableField(expandableObject.getId(), expandableObject); + } + } } @Override public void setResponseGetter(StripeResponseGetter responseGetter) { super.setResponseGetter(responseGetter); - trySetResponseGetter(filter, responseGetter); - trySetResponseGetter(usageThresholdConfig, responseGetter); + trySetResponseGetter(usageThreshold, responseGetter); } } diff --git a/src/main/java/com/stripe/model/billing/CreditBalanceSummary.java b/src/main/java/com/stripe/model/billing/CreditBalanceSummary.java new file mode 100644 index 00000000000..5a6e5a2d0d8 --- /dev/null +++ b/src/main/java/com/stripe/model/billing/CreditBalanceSummary.java @@ -0,0 +1,191 @@ +// File generated from our OpenAPI spec +package com.stripe.model.billing; + +import com.google.gson.annotations.SerializedName; +import com.stripe.exception.StripeException; +import com.stripe.model.Customer; +import com.stripe.model.ExpandableField; +import com.stripe.model.StripeObject; +import com.stripe.net.ApiRequest; +import com.stripe.net.ApiRequestParams; +import com.stripe.net.ApiResource; +import com.stripe.net.BaseAddress; +import com.stripe.net.RequestOptions; +import com.stripe.net.StripeResponseGetter; +import com.stripe.param.billing.CreditBalanceSummaryRetrieveParams; +import java.util.List; +import java.util.Map; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** Indicates the credit balance for credits granted to a customer. */ +@Getter +@Setter +@EqualsAndHashCode(callSuper = false) +public class CreditBalanceSummary extends ApiResource { + /** + * The credit balances. One entry per credit grant currency. If a customer only has credit grants + * in a single currency, then this will have a single balance entry. + */ + @SerializedName("balances") + List balances; + + /** The customer the balance is for. */ + @SerializedName("customer") + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + ExpandableField customer; + + /** + * Has the value {@code true} if the object exists in live mode or the value {@code false} if the + * object exists in test mode. + */ + @SerializedName("livemode") + Boolean livemode; + + /** + * String representing the object's type. Objects of the same type share the same value. + * + *

Equal to {@code billing.credit_balance_summary}. + */ + @SerializedName("object") + String object; + + /** Get ID of expandable {@code customer} object. */ + public String getCustomer() { + return (this.customer != null) ? this.customer.getId() : null; + } + + public void setCustomer(String id) { + this.customer = ApiResource.setExpandableFieldId(id, this.customer); + } + + /** Get expanded {@code customer}. */ + public Customer getCustomerObject() { + return (this.customer != null) ? this.customer.getExpanded() : null; + } + + public void setCustomerObject(Customer expandableObject) { + this.customer = new ExpandableField(expandableObject.getId(), expandableObject); + } + + /** Retrieves the credit balance summary for a customer. */ + public static CreditBalanceSummary retrieve() throws StripeException { + return retrieve((Map) null, (RequestOptions) null); + } + + /** Retrieves the credit balance summary for a customer. */ + public static CreditBalanceSummary retrieve(RequestOptions options) throws StripeException { + return retrieve((Map) null, options); + } + + /** Retrieves the credit balance summary for a customer. */ + public static CreditBalanceSummary retrieve(Map params, RequestOptions options) + throws StripeException { + String path = "/v1/billing/credit_balance_summary"; + ApiRequest request = + new ApiRequest(BaseAddress.API, ApiResource.RequestMethod.GET, path, params, options); + return getGlobalResponseGetter().request(request, CreditBalanceSummary.class); + } + + /** Retrieves the credit balance summary for a customer. */ + public static CreditBalanceSummary retrieve( + CreditBalanceSummaryRetrieveParams params, RequestOptions options) throws StripeException { + String path = "/v1/billing/credit_balance_summary"; + ApiResource.checkNullTypedParams(path, params); + ApiRequest request = + new ApiRequest( + BaseAddress.API, + ApiResource.RequestMethod.GET, + path, + ApiRequestParams.paramsToMap(params), + options); + return getGlobalResponseGetter().request(request, CreditBalanceSummary.class); + } + + @Getter + @Setter + @EqualsAndHashCode(callSuper = false) + public static class Balance extends StripeObject { + @SerializedName("available_balance") + AvailableBalance availableBalance; + + @SerializedName("ledger_balance") + LedgerBalance ledgerBalance; + + @Getter + @Setter + @EqualsAndHashCode(callSuper = false) + public static class AvailableBalance extends StripeObject { + /** The monetary amount. */ + @SerializedName("monetary") + Monetary monetary; + + /** + * The type of this amount. We currently only support {@code monetary} credits. + * + *

Equal to {@code monetary}. + */ + @SerializedName("type") + String type; + + @Getter + @Setter + @EqualsAndHashCode(callSuper = false) + public static class Monetary extends StripeObject { + /** + * Three-letter ISO currency + * code, in lowercase. Must be a supported + * currency. + */ + @SerializedName("currency") + String currency; + + /** A positive integer representing the amount. */ + @SerializedName("value") + Long value; + } + } + + @Getter + @Setter + @EqualsAndHashCode(callSuper = false) + public static class LedgerBalance extends StripeObject { + /** The monetary amount. */ + @SerializedName("monetary") + Monetary monetary; + + /** + * The type of this amount. We currently only support {@code monetary} credits. + * + *

Equal to {@code monetary}. + */ + @SerializedName("type") + String type; + + @Getter + @Setter + @EqualsAndHashCode(callSuper = false) + public static class Monetary extends StripeObject { + /** + * Three-letter ISO currency + * code, in lowercase. Must be a supported + * currency. + */ + @SerializedName("currency") + String currency; + + /** A positive integer representing the amount. */ + @SerializedName("value") + Long value; + } + } + } + + @Override + public void setResponseGetter(StripeResponseGetter responseGetter) { + super.setResponseGetter(responseGetter); + trySetResponseGetter(customer, responseGetter); + } +} diff --git a/src/main/java/com/stripe/model/billing/CreditBalanceTransaction.java b/src/main/java/com/stripe/model/billing/CreditBalanceTransaction.java new file mode 100644 index 00000000000..3fed5c315ce --- /dev/null +++ b/src/main/java/com/stripe/model/billing/CreditBalanceTransaction.java @@ -0,0 +1,346 @@ +// File generated from our OpenAPI spec +package com.stripe.model.billing; + +import com.google.gson.annotations.SerializedName; +import com.stripe.exception.StripeException; +import com.stripe.model.ExpandableField; +import com.stripe.model.HasId; +import com.stripe.model.Invoice; +import com.stripe.model.StripeObject; +import com.stripe.model.testhelpers.TestClock; +import com.stripe.net.ApiRequest; +import com.stripe.net.ApiRequestParams; +import com.stripe.net.ApiResource; +import com.stripe.net.BaseAddress; +import com.stripe.net.RequestOptions; +import com.stripe.net.StripeResponseGetter; +import com.stripe.param.billing.CreditBalanceTransactionListParams; +import com.stripe.param.billing.CreditBalanceTransactionRetrieveParams; +import java.util.Map; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * A credit balance transaction is a resource representing a transaction (either a credit or a + * debit) against an existing credit grant. + */ +@Getter +@Setter +@EqualsAndHashCode(callSuper = false) +public class CreditBalanceTransaction extends ApiResource implements HasId { + /** Time at which the object was created. Measured in seconds since the Unix epoch. */ + @SerializedName("created") + Long created; + + /** Credit details for this balance transaction. Only present if type is {@code credit}. */ + @SerializedName("credit") + Credit credit; + + /** The credit grant associated with this balance transaction. */ + @SerializedName("credit_grant") + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + ExpandableField creditGrant; + + /** Debit details for this balance transaction. Only present if type is {@code debit}. */ + @SerializedName("debit") + Debit debit; + + /** The effective time of this balance transaction. */ + @SerializedName("effective_at") + Long effectiveAt; + + /** Unique identifier for the object. */ + @Getter(onMethod_ = {@Override}) + @SerializedName("id") + String id; + + /** + * Has the value {@code true} if the object exists in live mode or the value {@code false} if the + * object exists in test mode. + */ + @SerializedName("livemode") + Boolean livemode; + + /** + * String representing the object's type. Objects of the same type share the same value. + * + *

Equal to {@code billing.credit_balance_transaction}. + */ + @SerializedName("object") + String object; + + /** ID of the test clock this credit balance transaction belongs to. */ + @SerializedName("test_clock") + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + ExpandableField testClock; + + /** + * The type of balance transaction (credit or debit). + * + *

One of {@code credit}, or {@code debit}. + */ + @SerializedName("type") + String type; + + /** Get ID of expandable {@code creditGrant} object. */ + public String getCreditGrant() { + return (this.creditGrant != null) ? this.creditGrant.getId() : null; + } + + public void setCreditGrant(String id) { + this.creditGrant = ApiResource.setExpandableFieldId(id, this.creditGrant); + } + + /** Get expanded {@code creditGrant}. */ + public CreditGrant getCreditGrantObject() { + return (this.creditGrant != null) ? this.creditGrant.getExpanded() : null; + } + + public void setCreditGrantObject(CreditGrant expandableObject) { + this.creditGrant = new ExpandableField(expandableObject.getId(), expandableObject); + } + + /** Get ID of expandable {@code testClock} object. */ + public String getTestClock() { + return (this.testClock != null) ? this.testClock.getId() : null; + } + + public void setTestClock(String id) { + this.testClock = ApiResource.setExpandableFieldId(id, this.testClock); + } + + /** Get expanded {@code testClock}. */ + public TestClock getTestClockObject() { + return (this.testClock != null) ? this.testClock.getExpanded() : null; + } + + public void setTestClockObject(TestClock expandableObject) { + this.testClock = new ExpandableField(expandableObject.getId(), expandableObject); + } + + /** Retrieve a list of credit balance transactions. */ + public static CreditBalanceTransactionCollection list(Map params) + throws StripeException { + return list(params, (RequestOptions) null); + } + + /** Retrieve a list of credit balance transactions. */ + public static CreditBalanceTransactionCollection list( + Map params, RequestOptions options) throws StripeException { + String path = "/v1/billing/credit_balance_transactions"; + ApiRequest request = + new ApiRequest(BaseAddress.API, ApiResource.RequestMethod.GET, path, params, options); + return getGlobalResponseGetter().request(request, CreditBalanceTransactionCollection.class); + } + + /** Retrieve a list of credit balance transactions. */ + public static CreditBalanceTransactionCollection list(CreditBalanceTransactionListParams params) + throws StripeException { + return list(params, (RequestOptions) null); + } + + /** Retrieve a list of credit balance transactions. */ + public static CreditBalanceTransactionCollection list( + CreditBalanceTransactionListParams params, RequestOptions options) throws StripeException { + String path = "/v1/billing/credit_balance_transactions"; + ApiResource.checkNullTypedParams(path, params); + ApiRequest request = + new ApiRequest( + BaseAddress.API, + ApiResource.RequestMethod.GET, + path, + ApiRequestParams.paramsToMap(params), + options); + return getGlobalResponseGetter().request(request, CreditBalanceTransactionCollection.class); + } + + /** Retrieves a credit balance transaction. */ + public static CreditBalanceTransaction retrieve(String id) throws StripeException { + return retrieve(id, (Map) null, (RequestOptions) null); + } + + /** Retrieves a credit balance transaction. */ + public static CreditBalanceTransaction retrieve(String id, RequestOptions options) + throws StripeException { + return retrieve(id, (Map) null, options); + } + + /** Retrieves a credit balance transaction. */ + public static CreditBalanceTransaction retrieve( + String id, Map params, RequestOptions options) throws StripeException { + String path = + String.format("/v1/billing/credit_balance_transactions/%s", ApiResource.urlEncodeId(id)); + ApiRequest request = + new ApiRequest(BaseAddress.API, ApiResource.RequestMethod.GET, path, params, options); + return getGlobalResponseGetter().request(request, CreditBalanceTransaction.class); + } + + /** Retrieves a credit balance transaction. */ + public static CreditBalanceTransaction retrieve( + String id, CreditBalanceTransactionRetrieveParams params, RequestOptions options) + throws StripeException { + String path = + String.format("/v1/billing/credit_balance_transactions/%s", ApiResource.urlEncodeId(id)); + ApiResource.checkNullTypedParams(path, params); + ApiRequest request = + new ApiRequest( + BaseAddress.API, + ApiResource.RequestMethod.GET, + path, + ApiRequestParams.paramsToMap(params), + options); + return getGlobalResponseGetter().request(request, CreditBalanceTransaction.class); + } + + @Getter + @Setter + @EqualsAndHashCode(callSuper = false) + public static class Credit extends StripeObject { + @SerializedName("amount") + Amount amount; + + /** + * The type of credit transaction. + * + *

Equal to {@code credits_granted}. + */ + @SerializedName("type") + String type; + + @Getter + @Setter + @EqualsAndHashCode(callSuper = false) + public static class Amount extends StripeObject { + /** The monetary amount. */ + @SerializedName("monetary") + Monetary monetary; + + /** + * The type of this amount. We currently only support {@code monetary} credits. + * + *

Equal to {@code monetary}. + */ + @SerializedName("type") + String type; + + @Getter + @Setter + @EqualsAndHashCode(callSuper = false) + public static class Monetary extends StripeObject { + /** + * Three-letter ISO currency + * code, in lowercase. Must be a supported + * currency. + */ + @SerializedName("currency") + String currency; + + /** A positive integer representing the amount. */ + @SerializedName("value") + Long value; + } + } + } + + @Getter + @Setter + @EqualsAndHashCode(callSuper = false) + public static class Debit extends StripeObject { + @SerializedName("amount") + Amount amount; + + /** + * Details of how the credits were applied to an invoice. Only present if {@code type} is {@code + * credits_applied}. + */ + @SerializedName("credits_applied") + CreditsApplied creditsApplied; + + /** + * The type of debit transaction. + * + *

One of {@code credits_applied}, {@code credits_expired}, or {@code credits_voided}. + */ + @SerializedName("type") + String type; + + @Getter + @Setter + @EqualsAndHashCode(callSuper = false) + public static class Amount extends StripeObject { + /** The monetary amount. */ + @SerializedName("monetary") + Monetary monetary; + + /** + * The type of this amount. We currently only support {@code monetary} credits. + * + *

Equal to {@code monetary}. + */ + @SerializedName("type") + String type; + + @Getter + @Setter + @EqualsAndHashCode(callSuper = false) + public static class Monetary extends StripeObject { + /** + * Three-letter ISO currency + * code, in lowercase. Must be a supported + * currency. + */ + @SerializedName("currency") + String currency; + + /** A positive integer representing the amount. */ + @SerializedName("value") + Long value; + } + } + + @Getter + @Setter + @EqualsAndHashCode(callSuper = false) + public static class CreditsApplied extends StripeObject { + /** The invoice to which the credits were applied. */ + @SerializedName("invoice") + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + ExpandableField invoice; + + /** The invoice line item to which the credits were applied. */ + @SerializedName("invoice_line_item") + String invoiceLineItem; + + /** Get ID of expandable {@code invoice} object. */ + public String getInvoice() { + return (this.invoice != null) ? this.invoice.getId() : null; + } + + public void setInvoice(String id) { + this.invoice = ApiResource.setExpandableFieldId(id, this.invoice); + } + + /** Get expanded {@code invoice}. */ + public Invoice getInvoiceObject() { + return (this.invoice != null) ? this.invoice.getExpanded() : null; + } + + public void setInvoiceObject(Invoice expandableObject) { + this.invoice = new ExpandableField(expandableObject.getId(), expandableObject); + } + } + } + + @Override + public void setResponseGetter(StripeResponseGetter responseGetter) { + super.setResponseGetter(responseGetter); + trySetResponseGetter(credit, responseGetter); + trySetResponseGetter(creditGrant, responseGetter); + trySetResponseGetter(debit, responseGetter); + trySetResponseGetter(testClock, responseGetter); + } +} diff --git a/src/main/java/com/stripe/model/billing/CreditBalanceTransactionCollection.java b/src/main/java/com/stripe/model/billing/CreditBalanceTransactionCollection.java new file mode 100644 index 00000000000..cd943c7dd0e --- /dev/null +++ b/src/main/java/com/stripe/model/billing/CreditBalanceTransactionCollection.java @@ -0,0 +1,7 @@ +// File generated from our OpenAPI spec +package com.stripe.model.billing; + +import com.stripe.model.StripeCollection; + +public class CreditBalanceTransactionCollection + extends StripeCollection {} diff --git a/src/main/java/com/stripe/model/billing/CreditGrant.java b/src/main/java/com/stripe/model/billing/CreditGrant.java new file mode 100644 index 00000000000..183537ad1ad --- /dev/null +++ b/src/main/java/com/stripe/model/billing/CreditGrant.java @@ -0,0 +1,448 @@ +// File generated from our OpenAPI spec +package com.stripe.model.billing; + +import com.google.gson.annotations.SerializedName; +import com.stripe.exception.StripeException; +import com.stripe.model.Customer; +import com.stripe.model.ExpandableField; +import com.stripe.model.HasId; +import com.stripe.model.MetadataStore; +import com.stripe.model.StripeObject; +import com.stripe.model.testhelpers.TestClock; +import com.stripe.net.ApiRequest; +import com.stripe.net.ApiRequestParams; +import com.stripe.net.ApiResource; +import com.stripe.net.BaseAddress; +import com.stripe.net.RequestOptions; +import com.stripe.net.StripeResponseGetter; +import com.stripe.param.billing.CreditGrantCreateParams; +import com.stripe.param.billing.CreditGrantExpireParams; +import com.stripe.param.billing.CreditGrantListParams; +import com.stripe.param.billing.CreditGrantRetrieveParams; +import com.stripe.param.billing.CreditGrantUpdateParams; +import com.stripe.param.billing.CreditGrantVoidGrantParams; +import java.util.Map; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** A credit grant is a resource that records a grant of some credit to a customer. */ +@Getter +@Setter +@EqualsAndHashCode(callSuper = false) +public class CreditGrant extends ApiResource implements HasId, MetadataStore { + @SerializedName("amount") + Amount amount; + + @SerializedName("applicability_config") + ApplicabilityConfig applicabilityConfig; + + /** + * The category of this credit grant. + * + *

One of {@code paid}, or {@code promotional}. + */ + @SerializedName("category") + String category; + + /** Time at which the object was created. Measured in seconds since the Unix epoch. */ + @SerializedName("created") + Long created; + + /** Id of the customer to whom the credit was granted. */ + @SerializedName("customer") + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + ExpandableField customer; + + /** The time when the credit becomes effective i.e when it is eligible to be used. */ + @SerializedName("effective_at") + Long effectiveAt; + + /** The time when the credit will expire. If not present, the credit will never expire. */ + @SerializedName("expires_at") + Long expiresAt; + + /** Unique identifier for the object. */ + @Getter(onMethod_ = {@Override}) + @SerializedName("id") + String id; + + /** + * Has the value {@code true} if the object exists in live mode or the value {@code false} if the + * object exists in test mode. + */ + @SerializedName("livemode") + Boolean livemode; + + /** + * Set of key-value pairs that you can attach + * to an object. This can be useful for storing additional information about the object in a + * structured format. + */ + @Getter(onMethod_ = {@Override}) + @SerializedName("metadata") + Map metadata; + + /** A descriptive name shown in dashboard and on invoices. */ + @SerializedName("name") + String name; + + /** + * String representing the object's type. Objects of the same type share the same value. + * + *

Equal to {@code billing.credit_grant}. + */ + @SerializedName("object") + String object; + + /** ID of the test clock this credit grant belongs to. */ + @SerializedName("test_clock") + @Getter(lombok.AccessLevel.NONE) + @Setter(lombok.AccessLevel.NONE) + ExpandableField testClock; + + /** Time at which the object was last updated. Measured in seconds since the Unix epoch. */ + @SerializedName("updated") + Long updated; + + /** + * The time when this credit grant was voided. If not present, the credit grant hasn't been + * voided. + */ + @SerializedName("voided_at") + Long voidedAt; + + /** Get ID of expandable {@code customer} object. */ + public String getCustomer() { + return (this.customer != null) ? this.customer.getId() : null; + } + + public void setCustomer(String id) { + this.customer = ApiResource.setExpandableFieldId(id, this.customer); + } + + /** Get expanded {@code customer}. */ + public Customer getCustomerObject() { + return (this.customer != null) ? this.customer.getExpanded() : null; + } + + public void setCustomerObject(Customer expandableObject) { + this.customer = new ExpandableField(expandableObject.getId(), expandableObject); + } + + /** Get ID of expandable {@code testClock} object. */ + public String getTestClock() { + return (this.testClock != null) ? this.testClock.getId() : null; + } + + public void setTestClock(String id) { + this.testClock = ApiResource.setExpandableFieldId(id, this.testClock); + } + + /** Get expanded {@code testClock}. */ + public TestClock getTestClockObject() { + return (this.testClock != null) ? this.testClock.getExpanded() : null; + } + + public void setTestClockObject(TestClock expandableObject) { + this.testClock = new ExpandableField(expandableObject.getId(), expandableObject); + } + + /** Creates a credit grant. */ + public static CreditGrant create(Map params) throws StripeException { + return create(params, (RequestOptions) null); + } + + /** Creates a credit grant. */ + public static CreditGrant create(Map params, RequestOptions options) + throws StripeException { + String path = "/v1/billing/credit_grants"; + ApiRequest request = + new ApiRequest(BaseAddress.API, ApiResource.RequestMethod.POST, path, params, options); + return getGlobalResponseGetter().request(request, CreditGrant.class); + } + + /** Creates a credit grant. */ + public static CreditGrant create(CreditGrantCreateParams params) throws StripeException { + return create(params, (RequestOptions) null); + } + + /** Creates a credit grant. */ + public static CreditGrant create(CreditGrantCreateParams params, RequestOptions options) + throws StripeException { + String path = "/v1/billing/credit_grants"; + ApiResource.checkNullTypedParams(path, params); + ApiRequest request = + new ApiRequest( + BaseAddress.API, + ApiResource.RequestMethod.POST, + path, + ApiRequestParams.paramsToMap(params), + options); + return getGlobalResponseGetter().request(request, CreditGrant.class); + } + + /** Expires a credit grant. */ + public CreditGrant expire() throws StripeException { + return expire((Map) null, (RequestOptions) null); + } + + /** Expires a credit grant. */ + public CreditGrant expire(RequestOptions options) throws StripeException { + return expire((Map) null, options); + } + + /** Expires a credit grant. */ + public CreditGrant expire(Map params) throws StripeException { + return expire(params, (RequestOptions) null); + } + + /** Expires a credit grant. */ + public CreditGrant expire(Map params, RequestOptions options) + throws StripeException { + String path = + String.format("/v1/billing/credit_grants/%s/expire", ApiResource.urlEncodeId(this.getId())); + ApiRequest request = + new ApiRequest(BaseAddress.API, ApiResource.RequestMethod.POST, path, params, options); + return getResponseGetter().request(request, CreditGrant.class); + } + + /** Expires a credit grant. */ + public CreditGrant expire(CreditGrantExpireParams params) throws StripeException { + return expire(params, (RequestOptions) null); + } + + /** Expires a credit grant. */ + public CreditGrant expire(CreditGrantExpireParams params, RequestOptions options) + throws StripeException { + String path = + String.format("/v1/billing/credit_grants/%s/expire", ApiResource.urlEncodeId(this.getId())); + ApiResource.checkNullTypedParams(path, params); + ApiRequest request = + new ApiRequest( + BaseAddress.API, + ApiResource.RequestMethod.POST, + path, + ApiRequestParams.paramsToMap(params), + options); + return getResponseGetter().request(request, CreditGrant.class); + } + + /** Retrieve a list of credit grants. */ + public static CreditGrantCollection list(Map params) throws StripeException { + return list(params, (RequestOptions) null); + } + + /** Retrieve a list of credit grants. */ + public static CreditGrantCollection list(Map params, RequestOptions options) + throws StripeException { + String path = "/v1/billing/credit_grants"; + ApiRequest request = + new ApiRequest(BaseAddress.API, ApiResource.RequestMethod.GET, path, params, options); + return getGlobalResponseGetter().request(request, CreditGrantCollection.class); + } + + /** Retrieve a list of credit grants. */ + public static CreditGrantCollection list(CreditGrantListParams params) throws StripeException { + return list(params, (RequestOptions) null); + } + + /** Retrieve a list of credit grants. */ + public static CreditGrantCollection list(CreditGrantListParams params, RequestOptions options) + throws StripeException { + String path = "/v1/billing/credit_grants"; + ApiResource.checkNullTypedParams(path, params); + ApiRequest request = + new ApiRequest( + BaseAddress.API, + ApiResource.RequestMethod.GET, + path, + ApiRequestParams.paramsToMap(params), + options); + return getGlobalResponseGetter().request(request, CreditGrantCollection.class); + } + + /** Retrieves a credit grant. */ + public static CreditGrant retrieve(String id) throws StripeException { + return retrieve(id, (Map) null, (RequestOptions) null); + } + + /** Retrieves a credit grant. */ + public static CreditGrant retrieve(String id, RequestOptions options) throws StripeException { + return retrieve(id, (Map) null, options); + } + + /** Retrieves a credit grant. */ + public static CreditGrant retrieve(String id, Map params, RequestOptions options) + throws StripeException { + String path = String.format("/v1/billing/credit_grants/%s", ApiResource.urlEncodeId(id)); + ApiRequest request = + new ApiRequest(BaseAddress.API, ApiResource.RequestMethod.GET, path, params, options); + return getGlobalResponseGetter().request(request, CreditGrant.class); + } + + /** Retrieves a credit grant. */ + public static CreditGrant retrieve( + String id, CreditGrantRetrieveParams params, RequestOptions options) throws StripeException { + String path = String.format("/v1/billing/credit_grants/%s", ApiResource.urlEncodeId(id)); + ApiResource.checkNullTypedParams(path, params); + ApiRequest request = + new ApiRequest( + BaseAddress.API, + ApiResource.RequestMethod.GET, + path, + ApiRequestParams.paramsToMap(params), + options); + return getGlobalResponseGetter().request(request, CreditGrant.class); + } + + /** Updates a credit grant. */ + @Override + public CreditGrant update(Map params) throws StripeException { + return update(params, (RequestOptions) null); + } + + /** Updates a credit grant. */ + @Override + public CreditGrant update(Map params, RequestOptions options) + throws StripeException { + String path = + String.format("/v1/billing/credit_grants/%s", ApiResource.urlEncodeId(this.getId())); + ApiRequest request = + new ApiRequest(BaseAddress.API, ApiResource.RequestMethod.POST, path, params, options); + return getResponseGetter().request(request, CreditGrant.class); + } + + /** Updates a credit grant. */ + public CreditGrant update(CreditGrantUpdateParams params) throws StripeException { + return update(params, (RequestOptions) null); + } + + /** Updates a credit grant. */ + public CreditGrant update(CreditGrantUpdateParams params, RequestOptions options) + throws StripeException { + String path = + String.format("/v1/billing/credit_grants/%s", ApiResource.urlEncodeId(this.getId())); + ApiResource.checkNullTypedParams(path, params); + ApiRequest request = + new ApiRequest( + BaseAddress.API, + ApiResource.RequestMethod.POST, + path, + ApiRequestParams.paramsToMap(params), + options); + return getResponseGetter().request(request, CreditGrant.class); + } + + /** Voids a credit grant. */ + public CreditGrant voidGrant() throws StripeException { + return voidGrant((Map) null, (RequestOptions) null); + } + + /** Voids a credit grant. */ + public CreditGrant voidGrant(RequestOptions options) throws StripeException { + return voidGrant((Map) null, options); + } + + /** Voids a credit grant. */ + public CreditGrant voidGrant(Map params) throws StripeException { + return voidGrant(params, (RequestOptions) null); + } + + /** Voids a credit grant. */ + public CreditGrant voidGrant(Map params, RequestOptions options) + throws StripeException { + String path = + String.format("/v1/billing/credit_grants/%s/void", ApiResource.urlEncodeId(this.getId())); + ApiRequest request = + new ApiRequest(BaseAddress.API, ApiResource.RequestMethod.POST, path, params, options); + return getResponseGetter().request(request, CreditGrant.class); + } + + /** Voids a credit grant. */ + public CreditGrant voidGrant(CreditGrantVoidGrantParams params) throws StripeException { + return voidGrant(params, (RequestOptions) null); + } + + /** Voids a credit grant. */ + public CreditGrant voidGrant(CreditGrantVoidGrantParams params, RequestOptions options) + throws StripeException { + String path = + String.format("/v1/billing/credit_grants/%s/void", ApiResource.urlEncodeId(this.getId())); + ApiResource.checkNullTypedParams(path, params); + ApiRequest request = + new ApiRequest( + BaseAddress.API, + ApiResource.RequestMethod.POST, + path, + ApiRequestParams.paramsToMap(params), + options); + return getResponseGetter().request(request, CreditGrant.class); + } + + @Getter + @Setter + @EqualsAndHashCode(callSuper = false) + public static class Amount extends StripeObject { + /** The monetary amount. */ + @SerializedName("monetary") + Monetary monetary; + + /** + * The type of this amount. We currently only support {@code monetary} credits. + * + *

Equal to {@code monetary}. + */ + @SerializedName("type") + String type; + + @Getter + @Setter + @EqualsAndHashCode(callSuper = false) + public static class Monetary extends StripeObject { + /** + * Three-letter ISO currency + * code, in lowercase. Must be a supported + * currency. + */ + @SerializedName("currency") + String currency; + + /** A positive integer representing the amount. */ + @SerializedName("value") + Long value; + } + } + + @Getter + @Setter + @EqualsAndHashCode(callSuper = false) + public static class ApplicabilityConfig extends StripeObject { + @SerializedName("scope") + Scope scope; + + @Getter + @Setter + @EqualsAndHashCode(callSuper = false) + public static class Scope extends StripeObject { + /** + * The price type to which credit grants can apply to. We currently only support {@code + * metered} price type. + * + *

Equal to {@code metered}. + */ + @SerializedName("price_type") + String priceType; + } + } + + @Override + public void setResponseGetter(StripeResponseGetter responseGetter) { + super.setResponseGetter(responseGetter); + trySetResponseGetter(amount, responseGetter); + trySetResponseGetter(applicabilityConfig, responseGetter); + trySetResponseGetter(customer, responseGetter); + trySetResponseGetter(testClock, responseGetter); + } +} diff --git a/src/main/java/com/stripe/model/billing/CreditGrantCollection.java b/src/main/java/com/stripe/model/billing/CreditGrantCollection.java new file mode 100644 index 00000000000..e2457267b2f --- /dev/null +++ b/src/main/java/com/stripe/model/billing/CreditGrantCollection.java @@ -0,0 +1,6 @@ +// File generated from our OpenAPI spec +package com.stripe.model.billing; + +import com.stripe.model.StripeCollection; + +public class CreditGrantCollection extends StripeCollection {} diff --git a/src/main/java/com/stripe/model/tax/Settings.java b/src/main/java/com/stripe/model/tax/Settings.java index 5c5a47854d8..3a21e2736ae 100644 --- a/src/main/java/com/stripe/model/tax/Settings.java +++ b/src/main/java/com/stripe/model/tax/Settings.java @@ -51,8 +51,7 @@ public class Settings extends ApiResource { String object; /** - * The {@code active} status indicates you have all required settings to calculate tax. A status - * can transition out of {@code active} when new required settings are introduced. + * The status of the Tax {@code Settings}. * *

One of {@code active}, or {@code pending}. */ diff --git a/src/main/java/com/stripe/model/treasury/ReceivedCredit.java b/src/main/java/com/stripe/model/treasury/ReceivedCredit.java index c79b6d1f1cc..1bf83826fdd 100644 --- a/src/main/java/com/stripe/model/treasury/ReceivedCredit.java +++ b/src/main/java/com/stripe/model/treasury/ReceivedCredit.java @@ -54,7 +54,8 @@ public class ReceivedCredit extends ApiResource implements HasId { * Reason for the failure. A ReceivedCredit might fail because the receiving FinancialAccount is * closed or frozen. * - *

One of {@code account_closed}, {@code account_frozen}, or {@code other}. + *

One of {@code account_closed}, {@code account_frozen}, {@code international_transaction}, or + * {@code other}. */ @SerializedName("failure_code") String failureCode; diff --git a/src/main/java/com/stripe/model/v2/Event.java b/src/main/java/com/stripe/model/v2/Event.java new file mode 100644 index 00000000000..feebc29bf71 --- /dev/null +++ b/src/main/java/com/stripe/model/v2/Event.java @@ -0,0 +1,154 @@ +// File generated from our OpenAPI spec +package com.stripe.model.v2; + +import com.google.gson.annotations.SerializedName; +import com.stripe.exception.StripeException; +import com.stripe.model.HasId; +import com.stripe.model.StripeActiveObject; +import com.stripe.model.StripeObject; +import com.stripe.model.StripeRawJsonObject; +import com.stripe.net.ApiRequest; +import com.stripe.net.ApiResource; +import com.stripe.net.BaseAddress; +import com.stripe.net.RequestOptions; +import com.stripe.net.StripeResponseGetter; +import java.time.Instant; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@EqualsAndHashCode(callSuper = false) +public class Event extends StripeObject implements HasId, StripeActiveObject { + /** Authentication context needed to fetch the event or related object. */ + @SerializedName("context") + String context; + + /** Time at which the object was created. */ + @SerializedName("created") + Instant created; + + /** Unique identifier for the event. */ + @Getter(onMethod_ = {@Override}) + @SerializedName("id") + String id; + + /** + * Has the value {@code true} if the object exists in live mode or the value {@code false} if the + * object exists in test mode. + */ + @SerializedName("livemode") + Boolean livemode; + + /** + * String representing the object's type. Objects of the same type share the same value of the + * object field. + * + *

Equal to {@code v2.core.event}. + */ + @SerializedName("object") + String object; + + /** Reason for the event. */ + @SerializedName("reason") + Reason reason; + + /** The type of the event. */ + @SerializedName("type") + String type; + + StripeResponseGetter responseGetter; + + @Override + public void setResponseGetter(StripeResponseGetter responseGetter) { + this.responseGetter = responseGetter; + } + + /** Retrieves the object associated with the event. */ + protected StripeObject fetchRelatedObject(RelatedObject relatedObject) throws StripeException { + if (relatedObject == null) { + return null; + } + if (relatedObject.getUrl() == null) { + return null; + } + + Class objectClass = + EventDataClassLookup.classLookup.get(relatedObject.getType()); + if (objectClass == null) { + objectClass = StripeRawJsonObject.class; + } + + RequestOptions opts = null; + + if (context != null) { + opts = new RequestOptions.RequestOptionsBuilder().setStripeAccount(context).build(); + } + + return this.responseGetter.request( + new ApiRequest( + BaseAddress.API, ApiResource.RequestMethod.GET, relatedObject.getUrl(), null, opts), + objectClass); + } + + /** + * Returns an StripeEvent instance using the provided JSON payload. Throws a JsonSyntaxException + * if the payload is not valid JSON. + * + * @param payload the payload sent by Stripe. + * @return the StripeEvent instance + */ + public static Event parse(String payload) { + return ApiResource.GSON.fromJson(payload, Event.class); + } + + @Getter + @Setter + @EqualsAndHashCode(callSuper = false) + public static class RelatedObject extends StripeObject implements HasId { + /** Unique identifier for the object relevant to the event. */ + @Getter(onMethod_ = {@Override}) + @SerializedName("id") + String id; + + /** Type of the object relevant to the event. */ + @SerializedName("type") + String type; + + /** Type of the object relevant to the event. */ + @SerializedName("url") + String url; + } + + @Getter + @Setter + @EqualsAndHashCode(callSuper = false) + public static class Reason extends StripeObject { + /** Information on the API request that instigated the event. */ + @SerializedName("request") + Request request; + + /** + * Event reason type. + * + *

Equal to {@code request}. + */ + @SerializedName("type") + String type; + + @Getter + @Setter + @EqualsAndHashCode(callSuper = false) + public static class Request extends StripeObject implements HasId { + /** ID of the API request that caused the event. */ + @Getter(onMethod_ = {@Override}) + @SerializedName("id") + String id; + + /** The idempotency key transmitted during the request. */ + @SerializedName("idempotency_key") + String idempotencyKey; + } + } +} diff --git a/src/main/java/com/stripe/model/v2/EventDataClassLookup.java b/src/main/java/com/stripe/model/v2/EventDataClassLookup.java new file mode 100644 index 00000000000..4a7127f82e1 --- /dev/null +++ b/src/main/java/com/stripe/model/v2/EventDataClassLookup.java @@ -0,0 +1,35 @@ +// File generated from our OpenAPI spec +package com.stripe.model.v2; + +import com.stripe.model.StripeObject; +import java.util.HashMap; +import java.util.Map; + +/** + * Event data class look up used in event deserialization. The key to look up is `object` string of + * the model. + * + *

For internal use by Stripe SDK only. + */ +public final class EventDataClassLookup { + public static final Map> classLookup = new HashMap<>(); + public static final Map> eventClassLookup = new HashMap<>(); + + static { + classLookup.put("billing.meter", com.stripe.model.billing.Meter.class); + + classLookup.put("billing.meter_event", com.stripe.model.v2.billing.MeterEvent.class); + classLookup.put( + "billing.meter_event_adjustment", com.stripe.model.v2.billing.MeterEventAdjustment.class); + classLookup.put( + "billing.meter_event_session", com.stripe.model.v2.billing.MeterEventSession.class); + + classLookup.put("v2.core.event", com.stripe.model.v2.Event.class); + + eventClassLookup.put( + "v1.billing.meter.error_report_triggered", + com.stripe.events.V1BillingMeterErrorReportTriggeredEvent.class); + eventClassLookup.put( + "v1.billing.meter.no_meter_found", com.stripe.events.V1BillingMeterNoMeterFoundEvent.class); + } +} diff --git a/src/main/java/com/stripe/model/v2/EventTypeAdapterFactory.java b/src/main/java/com/stripe/model/v2/EventTypeAdapterFactory.java new file mode 100644 index 00000000000..d8d56fc567a --- /dev/null +++ b/src/main/java/com/stripe/model/v2/EventTypeAdapterFactory.java @@ -0,0 +1,59 @@ +package com.stripe.model.v2; + +import com.google.gson.*; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.Map; + +public final class EventTypeAdapterFactory implements TypeAdapterFactory { + @Override + public TypeAdapter create(Gson gson, TypeToken type) { + if (type == null) { + return null; + } + + if (!Event.class.equals(type.getRawType())) { + return null; + } + + final TypeAdapter elementAdapter = gson.getAdapter(JsonElement.class); + final TypeAdapter fallbackAdapter = gson.getDelegateAdapter(this, type); + final Map> eventAdapters = new LinkedHashMap<>(); + + for (Map.Entry> entry : + EventDataClassLookup.eventClassLookup.entrySet()) { + TypeAdapter delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue())); + eventAdapters.put(entry.getKey(), delegate); + } + + return new TypeAdapter() { + @Override + @SuppressWarnings("unchecked") + public R read(JsonReader in) throws IOException { + JsonElement jsonElement = elementAdapter.read(in); + JsonElement typeElement = jsonElement.getAsJsonObject().get("type"); + + TypeAdapter selectedAdapter = null; + + if (typeElement != null && !typeElement.isJsonNull()) { + String eventType = typeElement.getAsString(); + selectedAdapter = (TypeAdapter) eventAdapters.get(eventType); + } + + if (selectedAdapter == null) { + selectedAdapter = fallbackAdapter; + } + + return selectedAdapter.fromJsonTree(jsonElement); + } + + @Override + public void write(JsonWriter out, R value) throws IOException { + throw new UnsupportedOperationException(); + } + }.nullSafe(); + } +} diff --git a/src/main/java/com/stripe/model/v2/StripeCollection.java b/src/main/java/com/stripe/model/v2/StripeCollection.java new file mode 100644 index 00000000000..dec37375d5a --- /dev/null +++ b/src/main/java/com/stripe/model/v2/StripeCollection.java @@ -0,0 +1,173 @@ +package com.stripe.model.v2; + +import com.google.gson.annotations.SerializedName; +import com.stripe.exception.StripeException; +import com.stripe.model.StripeActiveObject; +import com.stripe.model.StripeObject; +import com.stripe.model.StripeObjectInterface; +import com.stripe.net.*; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * Provides a representation of a single page worth of data from the Stripe API. + * + *

The following code will have the effect of iterating through a single page worth of invoice + * data retrieve from the API: + * + *

+ * + *

{@code
+ * foreach (Invoice invoice : Invoice.list(...).getData()) {
+ *   System.out.println("Current invoice = " + invoice.toString());
+ * }
+ * }
+ * + *

The class also provides a helper for iterating over collections that may be longer than a + * single page: + * + *

+ * + *

{@code
+ * foreach (Invoice invoice : Invoice.list(...).autoPagingIterable()) {
+ *   System.out.println("Current invoice = " + invoice.toString());
+ * }
+ * }
+ */ +@EqualsAndHashCode(callSuper = false) +public class StripeCollection extends StripeObject + implements StripeActiveObject { + private transient StripeResponseGetter responseGetter; + + @Setter private transient Type pageTypeToken; + + @Getter + @SerializedName("data") + List data; + + @Getter + @SerializedName("next_page_url") + String nextPageUrl; + + @Getter + @SerializedName("previous_page_url") + String previousPageUrl; + + @Getter @Setter private transient RequestOptions requestOptions; + + private static class Page { + List data; + String nextPageUrl; + + Page(List data, String nextPageUrl) { + this.data = data; + this.nextPageUrl = nextPageUrl; + } + } + + /** + * An Iterable implementation that starts from the StripeCollection data and will fetch next pages + * automatically. + */ + private class PagingIterable implements Iterable { + RequestOptions options; + + public PagingIterable() { + this.options = StripeCollection.this.getRequestOptions(); + } + + public PagingIterable(RequestOptions options) { + this.options = options; + } + + private Page getPage(String nextPageUrl) throws StripeException { + if (nextPageUrl == null) { + throw new IllegalArgumentException("nextPageUrl cannot be null"); + } + + StripeCollection response = + StripeCollection.this.responseGetter.request( + new ApiRequest( + BaseAddress.API, + ApiResource.RequestMethod.GET, + nextPageUrl, + new HashMap<>(), + this.options), + StripeCollection.this.pageTypeToken); + return new Page(response.getData(), response.getNextPageUrl()); + } + + @Override + public Iterator iterator() { + return new PagingIterator( + StripeCollection.this.getData(), StripeCollection.this.getNextPageUrl()); + } + + private class PagingIterator implements Iterator { + Iterator currentDataIterator; + String nextPageUrl; + + public PagingIterator(List currentPage, String nextPageUrl) { + this.currentDataIterator = currentPage.iterator(); + this.nextPageUrl = nextPageUrl; + } + + @Override + public T next() { + if (!currentDataIterator.hasNext() && this.nextPageUrl != null) { + try { + Page p = PagingIterable.this.getPage(this.nextPageUrl); + this.currentDataIterator = p.data.iterator(); + this.nextPageUrl = p.nextPageUrl; + } catch (final Exception e) { + throw new RuntimeException("Unable to paginate", e); + } + } + return this.currentDataIterator.next(); + } + + @Override + public boolean hasNext() { + return this.currentDataIterator.hasNext() || this.nextPageUrl != null; + } + } + } + + /** + * Constructs an iterable that can be used to iterate across all objects across all pages. As page + * boundaries are encountered, the next page will be fetched automatically for continued + * iteration. + * + *

This utilizes the options from the initial list request. + */ + public Iterable autoPagingIterable() { + return new PagingIterable(); + } + + /** + * Constructs an iterable that can be used to iterate across all objects across all pages. As page + * boundaries are encountered, the next page will be fetched automatically for continued + * iteration. + * + * @param options request options (will override the options from the initial list request) + */ + public Iterable autoPagingIterable(RequestOptions options) { + return new PagingIterable(options); + } + + @Override + public void setResponseGetter(StripeResponseGetter responseGetter) { + this.responseGetter = responseGetter; + + if (this.data != null) { + for (T item : data) { + trySetResponseGetter(item, responseGetter); + } + } + } +} diff --git a/src/main/java/com/stripe/model/v2/StripeDeletedObject.java b/src/main/java/com/stripe/model/v2/StripeDeletedObject.java new file mode 100644 index 00000000000..d1967ff21a4 --- /dev/null +++ b/src/main/java/com/stripe/model/v2/StripeDeletedObject.java @@ -0,0 +1,22 @@ +package com.stripe.model.v2; + +import com.google.gson.annotations.SerializedName; +import com.stripe.model.HasId; +import com.stripe.model.StripeObject; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@EqualsAndHashCode(callSuper = false) +public class StripeDeletedObject extends StripeObject implements HasId { + /** Unique identifier for the object. */ + @Getter(onMethod_ = {@Override}) + @SerializedName("id") + String id; + + /** String representing the object’s type. Objects of the same type share the same value. */ + @SerializedName("object") + String object; +} diff --git a/src/main/java/com/stripe/model/v2/billing/MeterEvent.java b/src/main/java/com/stripe/model/v2/billing/MeterEvent.java new file mode 100644 index 00000000000..71f069fa6d0 --- /dev/null +++ b/src/main/java/com/stripe/model/v2/billing/MeterEvent.java @@ -0,0 +1,61 @@ +// File generated from our OpenAPI spec +package com.stripe.model.v2.billing; + +import com.google.gson.annotations.SerializedName; +import com.stripe.model.StripeObject; +import java.time.Instant; +import java.util.Map; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@EqualsAndHashCode(callSuper = false) +public class MeterEvent extends StripeObject { + /** The creation time of this meter event. */ + @SerializedName("created") + Instant created; + + /** The name of the meter event. Corresponds with the {@code event_name} field on a meter. */ + @SerializedName("event_name") + String eventName; + + /** + * A unique identifier for the event. If not provided, one will be generated. We recommend using a + * globally unique identifier for this. We’ll enforce uniqueness within a rolling 24 hour period. + */ + @SerializedName("identifier") + String identifier; + + /** + * Has the value {@code true} if the object exists in live mode or the value {@code false} if the + * object exists in test mode. + */ + @SerializedName("livemode") + Boolean livemode; + + /** + * String representing the object's type. Objects of the same type share the same value of the + * object field. + * + *

Equal to {@code billing.meter_event}. + */ + @SerializedName("object") + String object; + + /** + * The payload of the event. This must contain the fields corresponding to a meter’s {@code + * customer_mapping.event_payload_key} (default is {@code stripe_customer_id}) and {@code + * value_settings.event_payload_key} (default is {@code value}). Read more about the payload. + */ + @SerializedName("payload") + Map payload; + + /** + * The time of the event. Must be within the past 35 calendar days or up to 5 minutes in the + * future. Defaults to current timestamp if not specified. + */ + @SerializedName("timestamp") + Instant timestamp; +} diff --git a/src/main/java/com/stripe/model/v2/billing/MeterEventAdjustment.java b/src/main/java/com/stripe/model/v2/billing/MeterEventAdjustment.java new file mode 100644 index 00000000000..5ae4b178f40 --- /dev/null +++ b/src/main/java/com/stripe/model/v2/billing/MeterEventAdjustment.java @@ -0,0 +1,77 @@ +// File generated from our OpenAPI spec +package com.stripe.model.v2.billing; + +import com.google.gson.annotations.SerializedName; +import com.stripe.model.HasId; +import com.stripe.model.StripeObject; +import java.time.Instant; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@EqualsAndHashCode(callSuper = false) +public class MeterEventAdjustment extends StripeObject implements HasId { + /** Specifies which event to cancel. */ + @SerializedName("cancel") + Cancel cancel; + + /** The time the adjustment was created. */ + @SerializedName("created") + Instant created; + + /** The name of the meter event. Corresponds with the {@code event_name} field on a meter. */ + @SerializedName("event_name") + String eventName; + + /** The unique id of this meter event adjustment. */ + @Getter(onMethod_ = {@Override}) + @SerializedName("id") + String id; + + /** + * Has the value {@code true} if the object exists in live mode or the value {@code false} if the + * object exists in test mode. + */ + @SerializedName("livemode") + Boolean livemode; + + /** + * String representing the object's type. Objects of the same type share the same value of the + * object field. + * + *

Equal to {@code billing.meter_event_adjustment}. + */ + @SerializedName("object") + String object; + + /** + * Open Enum. The meter event adjustment’s status. + * + *

One of {@code complete}, or {@code pending}. + */ + @SerializedName("status") + String status; + + /** + * Open Enum. Specifies whether to cancel a single event or a range of events for a time period. + * Time period cancellation is not supported yet. + * + *

Equal to {@code cancel}. + */ + @SerializedName("type") + String type; + + @Getter + @Setter + @EqualsAndHashCode(callSuper = false) + public static class Cancel extends StripeObject { + /** + * Unique identifier for the event. You can only cancel events within 24 hours of Stripe + * receiving them. + */ + @SerializedName("identifier") + String identifier; + } +} diff --git a/src/main/java/com/stripe/model/v2/billing/MeterEventSession.java b/src/main/java/com/stripe/model/v2/billing/MeterEventSession.java new file mode 100644 index 00000000000..a8b7a31fb48 --- /dev/null +++ b/src/main/java/com/stripe/model/v2/billing/MeterEventSession.java @@ -0,0 +1,51 @@ +// File generated from our OpenAPI spec +package com.stripe.model.v2.billing; + +import com.google.gson.annotations.SerializedName; +import com.stripe.model.HasId; +import com.stripe.model.StripeObject; +import java.time.Instant; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@EqualsAndHashCode(callSuper = false) +public class MeterEventSession extends StripeObject implements HasId { + /** + * The authentication token for this session. Use this token when calling the high-throughput + * meter event API. + */ + @SerializedName("authentication_token") + String authenticationToken; + + /** The creation time of this session. */ + @SerializedName("created") + Instant created; + + /** The time at which this session will expire. */ + @SerializedName("expires_at") + Instant expiresAt; + + /** The unique id of this auth session. */ + @Getter(onMethod_ = {@Override}) + @SerializedName("id") + String id; + + /** + * Has the value {@code true} if the object exists in live mode or the value {@code false} if the + * object exists in test mode. + */ + @SerializedName("livemode") + Boolean livemode; + + /** + * String representing the object's type. Objects of the same type share the same value of the + * object field. + * + *

Equal to {@code billing.meter_event_session}. + */ + @SerializedName("object") + String object; +} diff --git a/src/main/java/com/stripe/net/ApiMode.java b/src/main/java/com/stripe/net/ApiMode.java index dc30781d5ec..f9c7e340500 100644 --- a/src/main/java/com/stripe/net/ApiMode.java +++ b/src/main/java/com/stripe/net/ApiMode.java @@ -2,5 +2,5 @@ public enum ApiMode { V1, - OAuth + V2 } diff --git a/src/main/java/com/stripe/net/ApiRequest.java b/src/main/java/com/stripe/net/ApiRequest.java index 6c45769bf19..cc318fb9f46 100644 --- a/src/main/java/com/stripe/net/ApiRequest.java +++ b/src/main/java/com/stripe/net/ApiRequest.java @@ -19,21 +19,7 @@ private ApiRequest( Map params) { super(baseAddress, method, path, options, usage); this.params = params; - this.apiMode = ApiMode.V1; - } - - /** - * @deprecated This constructor is for backward compatibility and will be removed by Sept 30, 2024 - */ - @Deprecated - public ApiRequest( - BaseAddress baseAddress, - ApiResource.RequestMethod method, - String path, - Map params, - RequestOptions options, - ApiMode apiMode) { - this(baseAddress, method, path, options, null, params); + this.apiMode = path.startsWith("/v2") ? ApiMode.V2 : ApiMode.V1; } public ApiRequest( diff --git a/src/main/java/com/stripe/net/ApiRequestParams.java b/src/main/java/com/stripe/net/ApiRequestParams.java index b6ac6cb749f..679c5f847de 100644 --- a/src/main/java/com/stripe/net/ApiRequestParams.java +++ b/src/main/java/com/stripe/net/ApiRequestParams.java @@ -11,7 +11,7 @@ public abstract class ApiRequestParams { * Param key for an `extraParams` map. Any param/sub-param specifying a field intended to support * extra params from users should have the annotation * {@code @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY)}. Logic to handle this is in {@link - * ApiRequestParamsConverter}. + * ApiRequestParamsConverter}.t */ public static final String EXTRA_PARAMS_KEY = "_stripe_java_extra_param_key"; diff --git a/src/main/java/com/stripe/net/ApiRequestParamsConverter.java b/src/main/java/com/stripe/net/ApiRequestParamsConverter.java index 94b437d7435..79a08a054b3 100644 --- a/src/main/java/com/stripe/net/ApiRequestParamsConverter.java +++ b/src/main/java/com/stripe/net/ApiRequestParamsConverter.java @@ -11,10 +11,12 @@ import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; import com.stripe.Stripe; +import com.stripe.model.InstantSerializer; import com.stripe.param.common.EmptyParam; import java.io.IOException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.time.Instant; import java.util.Map; /** @@ -28,6 +30,7 @@ class ApiRequestParamsConverter { .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) .registerTypeAdapterFactory(new HasEmptyEnumTypeAdapterFactory()) .registerTypeAdapterFactory(new NullValuesInMapsTypeAdapterFactory()) + .registerTypeAdapter(Instant.class, new InstantSerializer()) .create(); private static final UntypedMapDeserializer FLATTENING_EXTRA_PARAMS_DESERIALIZER = diff --git a/src/main/java/com/stripe/net/ApiResource.java b/src/main/java/com/stripe/net/ApiResource.java index d9a4a85888e..214803bcca7 100644 --- a/src/main/java/com/stripe/net/ApiResource.java +++ b/src/main/java/com/stripe/net/ApiResource.java @@ -3,25 +3,29 @@ import com.google.gson.*; import com.stripe.exception.InvalidRequestException; import com.stripe.model.*; +import com.stripe.model.v2.EventTypeAdapterFactory; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.time.Instant; import java.util.Objects; public abstract class ApiResource extends StripeObject implements StripeActiveObject { public static final Charset CHARSET = StandardCharsets.UTF_8; + private static StripeResponseGetter globalResponseGetter = new LiveStripeResponseGetter(); + private transient StripeResponseGetter responseGetter; public static final Gson INTERNAL_GSON = createGson(false); public static final Gson GSON = createGson(true); - public static void setStripeResponseGetter(StripeResponseGetter srg) { + public static void setGlobalResponseGetter(StripeResponseGetter srg) { ApiResource.globalResponseGetter = srg; } - protected static StripeResponseGetter getGlobalResponseGetter() { + public static StripeResponseGetter getGlobalResponseGetter() { return ApiResource.globalResponseGetter; } @@ -51,6 +55,8 @@ private static Gson createGson(boolean shouldSetResponseGetter) { .registerTypeAdapter(Event.Data.class, new EventDataDeserializer()) .registerTypeAdapter(Event.Request.class, new EventRequestDeserializer()) .registerTypeAdapter(ExpandableField.class, new ExpandableFieldDeserializer()) + .registerTypeAdapter(Instant.class, new InstantDeserializer()) + .registerTypeAdapterFactory(new EventTypeAdapterFactory()) .registerTypeAdapter(StripeRawJsonObject.class, new StripeRawJsonObjectDeserializer()) .registerTypeAdapterFactory(new StripeCollectionItemTypeSettingFactory()) .addReflectionAccessFilter( diff --git a/src/main/java/com/stripe/net/Authenticator.java b/src/main/java/com/stripe/net/Authenticator.java new file mode 100644 index 00000000000..311e6860868 --- /dev/null +++ b/src/main/java/com/stripe/net/Authenticator.java @@ -0,0 +1,15 @@ +package com.stripe.net; + +import com.stripe.exception.StripeException; + +/** * Represents a request authentication mechanism. */ +public interface Authenticator { + /** + * * Authenticate the request + * + * @param request the request that need authentication. + * @return the request with authentication headers applied. + * @throws StripeException on authentication errors. + */ + StripeRequest authenticate(StripeRequest request) throws StripeException; +} diff --git a/src/main/java/com/stripe/net/BaseAddress.java b/src/main/java/com/stripe/net/BaseAddress.java index 8151df2df21..b241f5f0ab3 100644 --- a/src/main/java/com/stripe/net/BaseAddress.java +++ b/src/main/java/com/stripe/net/BaseAddress.java @@ -7,5 +7,7 @@ public enum BaseAddress { /** https://connect.stripe.com */ CONNECT, /** https://files.stripe.com */ - FILES + FILES, + /** https://events.stripe.com */ + METER_EVENTS } diff --git a/src/main/java/com/stripe/net/BearerTokenAuthenticator.java b/src/main/java/com/stripe/net/BearerTokenAuthenticator.java new file mode 100644 index 00000000000..03c9faa8d8b --- /dev/null +++ b/src/main/java/com/stripe/net/BearerTokenAuthenticator.java @@ -0,0 +1,46 @@ +package com.stripe.net; + +import com.stripe.exception.AuthenticationException; +import com.stripe.exception.StripeException; +import com.stripe.util.StringUtils; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode +public final class BearerTokenAuthenticator implements Authenticator { + private final String apiKey; + + public BearerTokenAuthenticator(String apiKey) { + if (apiKey == null) { + throw new IllegalArgumentException("apiKey should be not-null"); + } + this.apiKey = apiKey; + } + + public String getApiKey() { + return this.apiKey; + } + + @Override + public StripeRequest authenticate(StripeRequest request) throws StripeException { + if (apiKey.isEmpty()) { + throw new AuthenticationException( + "Your API key is invalid, as it is an empty string. You can double-check your API key " + + "from the Stripe Dashboard. See " + + "https://stripe.com/docs/api/authentication for details or contact support at " + + "https://support.stripe.com/email if you have any questions.", + null, + null, + 0); + } else if (StringUtils.containsWhitespace(apiKey)) { + throw new AuthenticationException( + "Your API key is invalid, as it contains whitespace. You can double-check your API key " + + "from the Stripe Dashboard. See " + + "https://stripe.com/docs/api/authentication for details or contact support at " + + "https://support.stripe.com/email if you have any questions.", + null, + null, + 0); + } + return request.withAdditionalHeader("Authorization", String.format("Bearer %s", apiKey)); + } +} diff --git a/src/main/java/com/stripe/net/FormEncoder.java b/src/main/java/com/stripe/net/FormEncoder.java index cb12a663972..d05e4be6ee3 100644 --- a/src/main/java/com/stripe/net/FormEncoder.java +++ b/src/main/java/com/stripe/net/FormEncoder.java @@ -22,7 +22,7 @@ public static HttpContent createHttpContent(Map params) throws I return HttpContent.buildFormURLEncodedContent(new ArrayList>()); } - Collection> flatParams = flattenParams(params); + Collection> flatParams = flattenParams(params, false); // If all parameters have been encoded as strings, then the content can be represented // with application/x-www-form-url-encoded encoding. Otherwise, use @@ -46,12 +46,24 @@ public static HttpContent createHttpContent(Map params) throws I * @return The query string. */ public static String createQueryString(Map params) { + return FormEncoder.createQueryString(params, false); + } + + /** + * Creates the HTTP query string for a given map of parameters. + * + * @param params The map of parameters. + * @param arraysAsRepeated Whether to encode arrays as repeated value ({@code a=1&a=2}) defaults + * to brackets encoding ({@code a[]=1,2}). + * @return The query string. + */ + public static String createQueryString(Map params, boolean arraysAsRepeated) { if (params == null) { return ""; } Collection> flatParams = - flattenParams(params).stream() + flattenParams(params, arraysAsRepeated).stream() .filter(kvp -> kvp.getValue() instanceof String) .map(kvp -> new KeyValuePair(kvp.getKey(), (String) kvp.getValue())) .collect(Collectors.toList()); @@ -105,10 +117,13 @@ public static String createQueryString( * } * * @param params The map of parameters. + * @param arraysAsRepeated Whether to encode arrays as repeated value ({@code a=1&a=2}) defaults + * to brackets encoding ({@code a[]=1,2}). * @return The flattened list of parameters. */ - public static List> flattenParams(Map params) { - return flattenParamsValue(params, null); + public static List> flattenParams( + Map params, boolean arraysAsRepeated) { + return flattenParamsValue(params, null, arraysAsRepeated); } /** @@ -142,10 +157,12 @@ private static String urlEncode(String value) { * * @param value The value for which to create the list of parameters. * @param keyPrefix The key under which new keys should be nested, if any. + * @param arraysAsRepeated Whether to encode arrays as repeated value ({@code a=1&a=2}) defaults + * to brackets encoding ({@code a[]=1,2}). * @return The list of parameters. */ private static List> flattenParamsValue( - Object value, String keyPrefix) { + Object value, String keyPrefix, boolean arraysAsRepeated) { List> flatParams = null; // I wish Java had pattern matching :( @@ -154,7 +171,7 @@ private static List> flattenParamsValue( flatParams = singleParam(keyPrefix, ""); } else if (value instanceof Map) { - flatParams = flattenParamsMap((Map) value, keyPrefix); + flatParams = flattenParamsMap((Map) value, keyPrefix, arraysAsRepeated); } else if (value instanceof String) { flatParams = singleParam(keyPrefix, value); @@ -166,12 +183,12 @@ private static List> flattenParamsValue( flatParams = singleParam(keyPrefix, value); } else if (value instanceof Collection) { - flatParams = flattenParamsCollection((Collection) value, keyPrefix); + flatParams = flattenParamsCollection((Collection) value, keyPrefix, arraysAsRepeated); } else if (value.getClass().isArray()) { Object[] array = getArrayForObject(value); Collection collection = Arrays.stream(array).collect(Collectors.toList()); - flatParams = flattenParamsCollection(collection, keyPrefix); + flatParams = flattenParamsCollection(collection, keyPrefix, arraysAsRepeated); } else if (value.getClass().isEnum()) { flatParams = @@ -194,7 +211,7 @@ private static List> flattenParamsValue( * @return The list of parameters. */ private static List> flattenParamsMap( - Map map, String keyPrefix) { + Map map, String keyPrefix, boolean arraysAsRepeated) { List> flatParams = new ArrayList>(); if (map == null) { return flatParams; @@ -206,7 +223,7 @@ private static List> flattenParamsMap( String newPrefix = newPrefix(key, keyPrefix); - flatParams.addAll(flattenParamsValue(value, newPrefix)); + flatParams.addAll(flattenParamsValue(value, newPrefix, arraysAsRepeated)); } return flatParams; @@ -219,10 +236,12 @@ private static List> flattenParamsMap( * * @param collection The collection for which to create the list of parameters. * @param keyPrefix The key under which new keys should be nested. + * @param arraysAsRepeated Whether to encode arrays as repeated value ({@code a=1&a=2}) defaults + * to brackets encoding ({@code a[]=1,2}). * @return The list of parameters. */ private static List> flattenParamsCollection( - Collection collection, String keyPrefix) { + Collection collection, String keyPrefix, boolean arraysAsRepeated) { List> flatParams = new ArrayList>(); if (collection == null) { return flatParams; @@ -230,15 +249,15 @@ private static List> flattenParamsCollection( int index = 0; for (Object value : collection) { - String newPrefix = String.format("%s[%d]", keyPrefix, index); - flatParams.addAll(flattenParamsValue(value, newPrefix)); + String newPrefix = arraysAsRepeated ? keyPrefix : String.format("%s[%d]", keyPrefix, index); + flatParams.addAll(flattenParamsValue(value, newPrefix, arraysAsRepeated)); index += 1; } /* Because application/x-www-form-urlencoded cannot represent an empty list, convention * is to take the list parameter and just set it to an empty string. (E.g. A regular * list might look like `a[0]=1&b[1]=2`. Emptying it would look like `a=`.) */ - if (flatParams.isEmpty()) { + if (!arraysAsRepeated && flatParams.isEmpty()) { flatParams.add(new KeyValuePair(keyPrefix, "")); } diff --git a/src/main/java/com/stripe/net/GlobalStripeResponseGetterOptions.java b/src/main/java/com/stripe/net/GlobalStripeResponseGetterOptions.java index 991b2cab9ca..9f9a76252a8 100644 --- a/src/main/java/com/stripe/net/GlobalStripeResponseGetterOptions.java +++ b/src/main/java/com/stripe/net/GlobalStripeResponseGetterOptions.java @@ -15,8 +15,11 @@ public class GlobalStripeResponseGetterOptions extends StripeResponseGetterOptio private GlobalStripeResponseGetterOptions() {} @Override - public String getApiKey() { - return Stripe.apiKey; + public Authenticator getAuthenticator() { + if (Stripe.apiKey == null) { + return null; + } + return new BearerTokenAuthenticator(Stripe.apiKey); } @Override @@ -63,4 +66,14 @@ public String getFilesBase() { public String getConnectBase() { return Stripe.getConnectBase(); } + + @Override + public String getMeterEventsBase() { + return Stripe.getMeterEventsBase(); + } + + @Override + public String getStripeContext() { + return null; + } } diff --git a/src/main/java/com/stripe/net/HttpContent.java b/src/main/java/com/stripe/net/HttpContent.java index bc17023c24c..1f3012ebf9e 100644 --- a/src/main/java/com/stripe/net/HttpContent.java +++ b/src/main/java/com/stripe/net/HttpContent.java @@ -7,6 +7,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.UUID; import lombok.Value; @@ -48,6 +49,20 @@ public static HttpContent buildFormURLEncodedContent( String.format("application/x-www-form-urlencoded;charset=%s", ApiResource.CHARSET)); } + /** + * Builds a new HttpContent for name/value tuples encoded using {@code + * application/x-www-form-urlencoded} MIME type. + * + * @param content the form-encoded content string + * @return the encoded HttpContent instance + * @throws IllegalArgumentException if nameValueCollection is null + */ + public static HttpContent buildFormURLEncodedContent(String content) throws IOException { + return new HttpContent( + content.getBytes(ApiResource.CHARSET), + String.format("application/x-www-form-urlencoded;charset=%s", ApiResource.CHARSET)); + } + /** The request's content, as a string. */ public String stringContent() { return new String(this.byteArrayContent, ApiResource.CHARSET); @@ -108,4 +123,15 @@ public static HttpContent buildMultipartFormDataContent( return new HttpContent( baos.toByteArray(), String.format("multipart/form-data; boundary=%s", boundary)); } + + /** + * Builds a new HttpContent for {@code application/json} MIME type. + * + * @param json the JSON value + * @return the encoded HttpContent instance + * @throws IllegalArgumentException if nameValueCollection is null + */ + public static HttpContent buildJsonContent(String json) { + return new HttpContent(json.getBytes(StandardCharsets.UTF_8), "application/json"); + } } diff --git a/src/main/java/com/stripe/net/JsonEncoder.java b/src/main/java/com/stripe/net/JsonEncoder.java new file mode 100644 index 00000000000..35de33bc6a2 --- /dev/null +++ b/src/main/java/com/stripe/net/JsonEncoder.java @@ -0,0 +1,23 @@ +package com.stripe.net; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +final class JsonEncoder { + private static final Gson BODY_GSON = + new GsonBuilder() + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) + .serializeNulls() + .create(); + + public static HttpContent createHttpContent(Map params) throws IOException { + if (params == null) { + params = new HashMap(); + } + return HttpContent.buildJsonContent(BODY_GSON.toJson(params)); + } +} diff --git a/src/main/java/com/stripe/net/LiveStripeResponseGetter.java b/src/main/java/com/stripe/net/LiveStripeResponseGetter.java index a8f52aaa54e..d40b1af01b3 100644 --- a/src/main/java/com/stripe/net/LiveStripeResponseGetter.java +++ b/src/main/java/com/stripe/net/LiveStripeResponseGetter.java @@ -1,10 +1,12 @@ package com.stripe.net; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import com.google.gson.JsonSyntaxException; import com.stripe.Stripe; import com.stripe.exception.*; +import com.stripe.exception.ApiKeyMissingException; import com.stripe.exception.oauth.InvalidClientException; import com.stripe.exception.oauth.InvalidGrantException; import com.stripe.exception.oauth.InvalidScopeException; @@ -64,6 +66,12 @@ public LiveStripeResponseGetter(HttpClient httpClient) { this(null, httpClient); } + /** + * Initializes a new instance of the {@link LiveStripeResponseGetter} class. + * + * @param options the client options instance to use + * @param httpClient the HTTP client to use + */ public LiveStripeResponseGetter(StripeResponseGetterOptions options, HttpClient httpClient) { this.options = options != null ? options : GlobalStripeResponseGetterOptions.INSTANCE; this.httpClient = (httpClient != null) ? httpClient : buildDefaultHttpClient(); @@ -75,7 +83,33 @@ private StripeRequest toStripeRequest(ApiRequest apiRequest, RequestOptions merg Optional telemetryHeaderValue = requestTelemetry.pollPayload(); StripeRequest request = - new StripeRequest(apiRequest.getMethod(), fullUrl, apiRequest.getParams(), mergedOptions); + StripeRequest.create( + apiRequest.getMethod(), + fullUrl, + apiRequest.getParams(), + mergedOptions, + apiRequest.getApiMode()); + if (telemetryHeaderValue.isPresent()) { + request = + request.withAdditionalHeader(RequestTelemetry.HEADER_NAME, telemetryHeaderValue.get()); + } + return request; + } + + private StripeRequest toRawStripeRequest(RawApiRequest apiRequest, RequestOptions mergedOptions) + throws StripeException { + + String fullUrl = fullUrl(apiRequest); + + Optional telemetryHeaderValue = requestTelemetry.pollPayload(); + StripeRequest request = + StripeRequest.createWithStringContent( + apiRequest.getMethod(), + fullUrl, + apiRequest.getRawContent(), + mergedOptions, + apiRequest.getApiMode()); + if (telemetryHeaderValue.isPresent()) { request = request.withAdditionalHeader(RequestTelemetry.HEADER_NAME, telemetryHeaderValue.get()); @@ -103,14 +137,14 @@ public T request(ApiRequest apiRequest, Type t String requestId = response.requestId(); if (responseCode < 200 || responseCode >= 300) { - handleError(response); + handleError(response, apiRequest.getApiMode()); } T resource = null; try { resource = (T) ApiResource.deserializeStripeObject(responseBody, typeToken, this); } catch (JsonSyntaxException e) { - raiseMalformedJsonError(responseBody, responseCode, requestId, e); + throw makeMalformedJsonError(responseBody, responseCode, requestId, e); } if (resource instanceof StripeCollectionInterface) { @@ -118,6 +152,11 @@ public T request(ApiRequest apiRequest, Type t ((StripeCollectionInterface) resource).setRequestParams(apiRequest.getParams()); } + if (resource instanceof com.stripe.model.v2.StripeCollection) { + ((com.stripe.model.v2.StripeCollection) resource) + .setRequestOptions(apiRequest.getOptions()); + } + resource.setLastResponse(response); return resource; @@ -152,12 +191,44 @@ public InputStream requestStream(ApiRequest apiRequest) throws StripeException { Stripe.getApiBase(), e.getMessage()), e); } - handleError(response); + handleError(response, apiRequest.getApiMode()); } return responseStream.body(); } + @Override + public StripeResponse rawRequest(RawApiRequest apiRequest) throws StripeException { + RequestOptions mergedOptions = RequestOptions.merge(this.options, apiRequest.getOptions()); + + if (RequestOptions.unsafeGetStripeVersionOverride(mergedOptions) != null) { + apiRequest = apiRequest.addUsage("unsafe_stripe_version_override"); + } + + StripeRequest request = toRawStripeRequest(apiRequest, mergedOptions); + + Map additionalHeaders = apiRequest.getOptions().getAdditionalHeaders(); + + if (additionalHeaders != null) { + for (Map.Entry entry : additionalHeaders.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + request = request.withAdditionalHeader(key, value); + } + } + + StripeResponse response = + sendWithTelemetry(request, apiRequest.getUsage(), r -> httpClient.requestWithRetries(r)); + + int responseCode = response.code(); + + if (responseCode < 200 || responseCode >= 300) { + handleError(response, apiRequest.getApiMode()); + } + + return response; + } + @Override @SuppressWarnings({"TypeParameterUnusedInFormals", "deprecation"}) public T request( @@ -189,7 +260,7 @@ private static HttpClient buildDefaultHttpClient() { return new HttpURLConnectionClient(); } - private static void raiseMalformedJsonError( + private static ApiException makeMalformedJsonError( String responseBody, int responseCode, String requestId, Throwable e) throws ApiException { String details = e == null ? "none" : e.getMessage(); throw new ApiException( @@ -202,7 +273,22 @@ private static void raiseMalformedJsonError( e); } - private void handleError(StripeResponse response) throws StripeException { + private StripeError parseStripeError( + String body, int code, String requestId, Class klass) + throws StripeException { + StripeError ret; + try { + JsonObject jsonObject = + ApiResource.GSON.fromJson(body, JsonObject.class).getAsJsonObject("error"); + ret = (StripeError) StripeObject.deserializeStripeObject(jsonObject, klass, this); + if (ret != null) return ret; + } catch (JsonSyntaxException e) { + throw makeMalformedJsonError(body, code, requestId, e); + } + throw makeMalformedJsonError(body, code, requestId, null); + } + + private void handleError(StripeResponse response, ApiMode apiMode) throws StripeException { JsonObject responseBody = ApiResource.GSON.fromJson(response.body(), JsonObject.class); /* @@ -215,30 +301,20 @@ private void handleError(StripeResponse response) throws StripeException { if (error.isString()) { handleOAuthError(response); } + } else if (apiMode == ApiMode.V2) { + handleV2ApiError(response); } else { - handleApiError(response); + handleV1ApiError(response); } } - private void handleApiError(StripeResponse response) throws StripeException { - StripeError error = null; + private void handleV1ApiError(StripeResponse response) throws StripeException { StripeException exception = null; - try { - JsonObject jsonObject = - ApiResource.INTERNAL_GSON - .fromJson(response.body(), JsonObject.class) - .getAsJsonObject("error"); - error = ApiResource.deserializeStripeObject(jsonObject.toString(), StripeError.class, this); - } catch (JsonSyntaxException e) { - raiseMalformedJsonError(response.body(), response.code(), response.requestId(), e); - } - if (error == null) { - raiseMalformedJsonError(response.body(), response.code(), response.requestId(), null); - } + StripeError error = + parseStripeError(response.body(), response.code(), response.requestId(), StripeError.class); error.setLastResponse(response); - switch (response.code()) { case 400: case 404: @@ -295,23 +371,59 @@ private void handleApiError(StripeResponse response) throws StripeException { error.getMessage(), response.requestId(), error.getCode(), response.code(), null); break; } - exception.setStripeError(error); throw exception; } + private void handleV2ApiError(StripeResponse response) throws StripeException { + JsonObject body = + ApiResource.GSON.fromJson(response.body(), JsonObject.class).getAsJsonObject("error"); + + JsonElement typeElement = body == null ? null : body.get("type"); + JsonElement codeElement = body == null ? null : body.get("code"); + String type = typeElement == null ? "" : typeElement.getAsString(); + String code = codeElement == null ? "" : codeElement.getAsString(); + + StripeException exception = + StripeException.parseV2Exception(type, body, response.code(), response.requestId(), this); + if (exception != null) { + throw exception; + } + + StripeError error; + try { + error = + parseStripeError( + response.body(), response.code(), response.requestId(), StripeError.class); + } catch (ApiException e) { + String message = "Unrecognized error type '" + type + "'"; + JsonElement messageField = body == null ? null : body.get("message"); + if (messageField != null && messageField.isJsonPrimitive()) { + message = messageField.getAsString(); + } + + throw new ApiException(message, response.requestId(), code, response.code(), null); + } + + error.setLastResponse(response); + exception = + new ApiException(error.getMessage(), response.requestId(), code, response.code(), null); + exception.setStripeError(error); + throw exception; + } + private void handleOAuthError(StripeResponse response) throws StripeException { OAuthError error = null; StripeException exception = null; try { - error = ApiResource.deserializeStripeObject(response.body(), OAuthError.class, this); + error = StripeObject.deserializeStripeObject(response.body(), OAuthError.class, this); } catch (JsonSyntaxException e) { - raiseMalformedJsonError(response.body(), response.code(), response.requestId(), e); + throw makeMalformedJsonError(response.body(), response.code(), response.requestId(), e); } if (error == null) { - raiseMalformedJsonError(response.body(), response.code(), response.requestId(), null); + throw makeMalformedJsonError(response.body(), response.code(), response.requestId(), null); } error.setLastResponse(response); @@ -364,7 +476,8 @@ private void handleOAuthError(StripeResponse response) throws StripeException { @Override public void validateRequestOptions(RequestOptions options) { - if ((options == null || options.getApiKey() == null) && this.options.getApiKey() == null) { + if ((options == null || options.getAuthenticator() == null) + && this.options.getAuthenticator() == null) { throw new ApiKeyMissingException( "API key is not set. You can set the API key globally using Stripe.ApiKey, or by passing RequestOptions"); } @@ -385,6 +498,9 @@ private String fullUrl(BaseApiRequest apiRequest) { case FILES: baseUrl = this.options.getFilesBase(); break; + case METER_EVENTS: + baseUrl = this.options.getMeterEventsBase(); + break; default: throw new IllegalArgumentException("Unknown base address " + baseAddress); } diff --git a/src/main/java/com/stripe/net/OAuth.java b/src/main/java/com/stripe/net/OAuth.java index 31d979e8acf..776345ad60f 100644 --- a/src/main/java/com/stripe/net/OAuth.java +++ b/src/main/java/com/stripe/net/OAuth.java @@ -63,7 +63,6 @@ public static DeauthorizedAccount deauthorize(Map params, Reques throws StripeException { Map paramsCopy = new HashMap<>(); paramsCopy.putAll(params); - paramsCopy.put("client_id", getClientId(paramsCopy, options)); ApiRequest request = new ApiRequest( diff --git a/src/main/java/com/stripe/net/RawApiRequest.java b/src/main/java/com/stripe/net/RawApiRequest.java new file mode 100644 index 00000000000..10ff94aafdd --- /dev/null +++ b/src/main/java/com/stripe/net/RawApiRequest.java @@ -0,0 +1,51 @@ +package com.stripe.net; + +import java.util.ArrayList; +import java.util.List; +import lombok.Getter; + +public class RawApiRequest extends BaseApiRequest { + @Getter(onMethod_ = {@Override}) + private RawRequestOptions options; + + @Getter private String rawContent; + + @Getter private final ApiMode apiMode; + + private RawApiRequest( + BaseAddress baseAddress, + ApiResource.RequestMethod method, + String path, + RawRequestOptions options, + List usage, + String rawContent) { + super(baseAddress, method, path, options, usage); + this.rawContent = rawContent; + this.options = options; + this.apiMode = path.startsWith("/v2") ? ApiMode.V2 : ApiMode.V1; + } + + public RawApiRequest( + BaseAddress baseAddress, + ApiResource.RequestMethod method, + String path, + String rawContent, + RawRequestOptions options) { + this(baseAddress, method, path, options, null, rawContent); + } + + public RawApiRequest addUsage(String usage) { + List newUsage = new ArrayList<>(); + if (this.getUsage() != null) { + newUsage.addAll(this.getUsage()); + } + newUsage.add(usage); + return new RawApiRequest( + this.getBaseAddress(), + this.getMethod(), + this.getPath(), + this.getOptions(), + newUsage, + this.getRawContent()); + } +} diff --git a/src/main/java/com/stripe/net/RawRequestOptions.java b/src/main/java/com/stripe/net/RawRequestOptions.java new file mode 100644 index 00000000000..abad1823552 --- /dev/null +++ b/src/main/java/com/stripe/net/RawRequestOptions.java @@ -0,0 +1,144 @@ +package com.stripe.net; + +import java.net.PasswordAuthentication; +import java.net.Proxy; +import java.util.Map; + +public class RawRequestOptions extends RequestOptions { + private Map additionalHeaders; + + public RawRequestOptions( + Authenticator authenticator, + String clientId, + String idempotencyKey, + String stripeContext, + String stripeAccount, + String stripeVersionOverride, + String baseUrl, + Integer connectTimeout, + Integer readTimeout, + Integer maxNetworkRetries, + Proxy connectionProxy, + PasswordAuthentication proxyCredential, + Map additionalHeaders) { + super( + authenticator, + clientId, + idempotencyKey, + stripeContext, + stripeAccount, + stripeVersionOverride, + baseUrl, + connectTimeout, + readTimeout, + maxNetworkRetries, + connectionProxy, + proxyCredential); + this.additionalHeaders = additionalHeaders; + } + + public Map getAdditionalHeaders() { + return additionalHeaders; + } + + public static RawRequestOptionsBuilder builder() { + return new RawRequestOptionsBuilder(); + } + + public static final class RawRequestOptionsBuilder extends RequestOptions.RequestOptionsBuilder { + private Map additionalHeaders; + + public Map getAdditionalHeaders() { + return this.additionalHeaders; + } + + public RawRequestOptionsBuilder setAdditionalHeaders(Map additionalHeaders) { + this.additionalHeaders = additionalHeaders; + return this; + } + + @Override + public RawRequestOptionsBuilder setApiKey(String apiKey) { + super.setApiKey(apiKey); + return this; + } + + @Override + public RawRequestOptionsBuilder setClientId(String clientId) { + super.setClientId(clientId); + return this; + } + + @Override + public RawRequestOptionsBuilder setIdempotencyKey(String idempotencyKey) { + super.setIdempotencyKey(idempotencyKey); + return this; + } + + @Override + public RawRequestOptionsBuilder setStripeContext(String stripeContext) { + super.setStripeContext(stripeContext); + return this; + } + + @Override + public RawRequestOptionsBuilder setStripeAccount(String stripeAccount) { + super.setStripeAccount(stripeAccount); + return this; + } + + @Override + public RawRequestOptionsBuilder setBaseUrl(String baseUrl) { + super.setBaseUrl(baseUrl); + return this; + } + + @Override + public RawRequestOptionsBuilder setConnectTimeout(Integer timeout) { + super.setConnectTimeout(timeout); + return this; + } + + @Override + public RawRequestOptionsBuilder setReadTimeout(Integer timeout) { + super.setReadTimeout(timeout); + return this; + } + + @Override + public RawRequestOptionsBuilder setMaxNetworkRetries(Integer maxNetworkRetries) { + super.setMaxNetworkRetries(maxNetworkRetries); + return this; + } + + @Override + public RawRequestOptionsBuilder setConnectionProxy(Proxy connectionProxy) { + super.setConnectionProxy(connectionProxy); + return this; + } + + @Override + public RawRequestOptionsBuilder setProxyCredential(PasswordAuthentication proxyCredential) { + super.setProxyCredential(proxyCredential); + return this; + } + + @Override + public RawRequestOptions build() { + return new RawRequestOptions( + authenticator, + normalizeClientId(this.clientId), + normalizeIdempotencyKey(this.idempotencyKey), + normalizeStripeContext(this.stripeContext), + normalizeStripeAccount(this.stripeAccount), + normalizeStripeVersion(this.stripeVersionOverride), + normalizeBaseUrl(this.baseUrl), + connectTimeout, + readTimeout, + maxNetworkRetries, + connectionProxy, + proxyCredential, + additionalHeaders); + } + } +} diff --git a/src/main/java/com/stripe/net/RequestOptions.java b/src/main/java/com/stripe/net/RequestOptions.java index aa6519e0739..4e83d396585 100644 --- a/src/main/java/com/stripe/net/RequestOptions.java +++ b/src/main/java/com/stripe/net/RequestOptions.java @@ -8,8 +8,11 @@ @EqualsAndHashCode(callSuper = false) public class RequestOptions { - private final String apiKey; + // When adding setting here keep them in sync with settings in StripeClientOptions and + // in the RequestOptions.merge method + private final Authenticator authenticator; private final String clientId; + private final String stripeContext; private final String idempotencyKey; private final String stripeAccount; private final String baseUrl; @@ -28,13 +31,15 @@ public class RequestOptions { private final PasswordAuthentication proxyCredential; public static RequestOptions getDefault() { - return new RequestOptions(null, null, null, null, null, null, null, null, null, null, null); + return new RequestOptions( + null, null, null, null, null, null, null, null, null, null, null, null); } - private RequestOptions( - String apiKey, + protected RequestOptions( + Authenticator authenticator, String clientId, String idempotencyKey, + String stripeContext, String stripeAccount, String stripeVersionOverride, String baseUrl, @@ -43,9 +48,10 @@ private RequestOptions( Integer maxNetworkRetries, Proxy connectionProxy, PasswordAuthentication proxyCredential) { - this.apiKey = apiKey; + this.authenticator = authenticator; this.clientId = clientId; this.idempotencyKey = idempotencyKey; + this.stripeContext = stripeContext; this.stripeAccount = stripeAccount; this.stripeVersionOverride = stripeVersionOverride; this.baseUrl = baseUrl; @@ -56,14 +62,26 @@ private RequestOptions( this.proxyCredential = proxyCredential; } + public Authenticator getAuthenticator() { + return this.authenticator; + } + public String getApiKey() { - return apiKey; + if (authenticator instanceof BearerTokenAuthenticator) { + return ((BearerTokenAuthenticator) authenticator).getApiKey(); + } + + return null; } public String getClientId() { return clientId; } + public String getStripeContext() { + return stripeContext; + } + public String getIdempotencyKey() { return idempotencyKey; } @@ -117,7 +135,9 @@ public static RequestOptionsBuilder builder() { */ @Deprecated public RequestOptionsBuilder toBuilder() { - return new RequestOptionsBuilder().setApiKey(this.apiKey).setStripeAccount(this.stripeAccount); + return new RequestOptionsBuilder() + .setAuthenticator(this.authenticator) + .setStripeAccount(this.stripeAccount); } /** @@ -128,7 +148,7 @@ public RequestOptionsBuilder toBuilder() { public RequestOptionsBuilder toBuilderFullCopy() { return RequestOptionsBuilder.unsafeSetStripeVersionOverride( new RequestOptionsBuilder() - .setApiKey(this.apiKey) + .setAuthenticator(this.authenticator) .setBaseUrl(this.baseUrl) .setClientId(this.clientId) .setIdempotencyKey(this.idempotencyKey) @@ -141,18 +161,19 @@ public RequestOptionsBuilder toBuilderFullCopy() { stripeVersionOverride); } - public static final class RequestOptionsBuilder { - private String apiKey; - private String clientId; - private String idempotencyKey; - private String stripeAccount; - private String stripeVersionOverride; - private Integer connectTimeout; - private Integer readTimeout; - private Integer maxNetworkRetries; - private Proxy connectionProxy; - private PasswordAuthentication proxyCredential; - private String baseUrl; + public static class RequestOptionsBuilder { + protected Authenticator authenticator; + protected String clientId; + protected String idempotencyKey; + protected String stripeContext; + protected String stripeAccount; + protected String stripeVersionOverride; + protected Integer connectTimeout; + protected Integer readTimeout; + protected Integer maxNetworkRetries; + protected Proxy connectionProxy; + protected PasswordAuthentication proxyCredential; + protected String baseUrl; /** * Constructs a request options builder with the global parameters (API key and client ID) as @@ -160,17 +181,34 @@ public static final class RequestOptionsBuilder { */ public RequestOptionsBuilder() {} + public Authenticator getAuthenticator() { + return this.authenticator; + } + + public RequestOptionsBuilder setAuthenticator(Authenticator authenticator) { + this.authenticator = authenticator; + return this; + } + public String getApiKey() { - return apiKey; + if (authenticator instanceof BearerTokenAuthenticator) { + return ((BearerTokenAuthenticator) authenticator).getApiKey(); + } + + return null; } public RequestOptionsBuilder setApiKey(String apiKey) { - this.apiKey = normalizeApiKey(apiKey); + if (apiKey == null) { + this.authenticator = null; + } else { + this.authenticator = new BearerTokenAuthenticator(normalizeApiKey(apiKey)); + } return this; } public RequestOptionsBuilder clearApiKey() { - this.apiKey = null; + this.authenticator = null; return this; } @@ -188,12 +226,26 @@ public RequestOptionsBuilder clearClientId() { return this; } + public String getStripeContext() { + return stripeContext; + } + + public RequestOptionsBuilder setStripeContext(String context) { + this.stripeContext = context; + return this; + } + + public RequestOptionsBuilder clearStripeContext() { + this.stripeContext = null; + return this; + } + public RequestOptionsBuilder setIdempotencyKey(String idempotencyKey) { this.idempotencyKey = idempotencyKey; return this; } - public int getConnectTimeout() { + public Integer getConnectTimeout() { return connectTimeout; } @@ -304,9 +356,10 @@ public RequestOptionsBuilder setBaseUrl(final String baseUrl) { /** Constructs a {@link RequestOptions} with the specified values. */ public RequestOptions build() { return new RequestOptions( - normalizeApiKey(this.apiKey), + this.authenticator, normalizeClientId(this.clientId), normalizeIdempotencyKey(this.idempotencyKey), + stripeContext, normalizeStripeAccount(this.stripeAccount), normalizeStripeVersion(this.stripeVersionOverride), normalizeBaseUrl(this.baseUrl), @@ -318,7 +371,7 @@ public RequestOptions build() { } } - private static String normalizeApiKey(String apiKey) { + protected static String normalizeApiKey(String apiKey) { // null apiKeys are considered "valid" if (apiKey == null) { return null; @@ -326,7 +379,7 @@ private static String normalizeApiKey(String apiKey) { return apiKey.trim(); } - private static String normalizeClientId(String clientId) { + protected static String normalizeClientId(String clientId) { // null client_ids are considered "valid" if (clientId == null) { return null; @@ -338,7 +391,7 @@ private static String normalizeClientId(String clientId) { return normalized; } - private static String normalizeStripeVersion(String stripeVersion) { + protected static String normalizeStripeVersion(String stripeVersion) { // null stripeVersions are considered "valid" and use Stripe.apiVersion if (stripeVersion == null) { return null; @@ -350,7 +403,7 @@ private static String normalizeStripeVersion(String stripeVersion) { return normalized; } - private static String normalizeBaseUrl(String baseUrl) { + protected static String normalizeBaseUrl(String baseUrl) { // null baseUrl is valid, and will fall back to e.g. Stripe.apiBase or Stripe.connectBase // (depending on the method) if (baseUrl == null) { @@ -363,7 +416,7 @@ private static String normalizeBaseUrl(String baseUrl) { return normalized; } - private static String normalizeIdempotencyKey(String idempotencyKey) { + protected static String normalizeIdempotencyKey(String idempotencyKey) { if (idempotencyKey == null) { return null; } @@ -380,7 +433,18 @@ private static String normalizeIdempotencyKey(String idempotencyKey) { return normalized; } - private static String normalizeStripeAccount(String stripeAccount) { + protected static String normalizeStripeContext(String stripContext) { + if (stripContext == null) { + return null; + } + String normalized = stripContext.trim(); + if (normalized.isEmpty()) { + throw new InvalidRequestOptionsException("Empty stripe context specified!"); + } + return normalized; + } + + protected static String normalizeStripeAccount(String stripeAccount) { if (stripeAccount == null) { return null; } @@ -394,9 +458,10 @@ private static String normalizeStripeAccount(String stripeAccount) { static RequestOptions merge(StripeResponseGetterOptions clientOptions, RequestOptions options) { if (options == null) { return new RequestOptions( - clientOptions.getApiKey(), // authenticator + clientOptions.getAuthenticator(), // authenticator clientOptions.getClientId(), // clientId null, // idempotencyKey + clientOptions.getStripeContext(), // stripeContext null, // stripeAccount null, // stripeVersionOverride null, // baseUrl @@ -408,9 +473,14 @@ static RequestOptions merge(StripeResponseGetterOptions clientOptions, RequestOp ); } return new RequestOptions( - options.getApiKey() != null ? options.getApiKey() : clientOptions.getApiKey(), + options.getAuthenticator() != null + ? options.getAuthenticator() + : clientOptions.getAuthenticator(), options.getClientId() != null ? options.getClientId() : clientOptions.getClientId(), options.getIdempotencyKey(), + options.getStripeContext() != null + ? options.getStripeContext() + : clientOptions.getStripeContext(), options.getStripeAccount(), RequestOptions.unsafeGetStripeVersionOverride(options), options.getBaseUrl(), diff --git a/src/main/java/com/stripe/net/RequestSigningAuthenticator.java b/src/main/java/com/stripe/net/RequestSigningAuthenticator.java new file mode 100644 index 00000000000..5b038176347 --- /dev/null +++ b/src/main/java/com/stripe/net/RequestSigningAuthenticator.java @@ -0,0 +1,167 @@ +package com.stripe.net; + +import com.stripe.exception.AuthenticationException; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; +import java.util.List; + +public abstract class RequestSigningAuthenticator implements Authenticator { + @FunctionalInterface + interface CurrentTimeInSecondsGetter { + Long getCurrentTimeInSeconds(); + } + + private CurrentTimeInSecondsGetter currentTimeInSecondsGetter = + new CurrentTimeInSecondsGetter() { + @Override + public Long getCurrentTimeInSeconds() { + return java.time.Clock.systemUTC().millis() / 1000; + } + }; + private static final String authorizationHeaderName = "Authorization"; + private static final String stripeContextHeaderName = "Stripe-Context"; + private static final String stripeAccountHeaderName = "Stripe-Account"; + private static final String contentDigestHeaderName = "Content-Digest"; + private static final String signatureInputHeaderName = "Signature-Input"; + private static final String signatureHeaderName = "Signature"; + private static final String[] coveredHeaders = + new String[] { + "Content-Type", + contentDigestHeaderName, + stripeContextHeaderName, + stripeAccountHeaderName, + authorizationHeaderName + }; + + private static final String[] coveredHeadersGet = + new String[] {stripeContextHeaderName, stripeAccountHeaderName, authorizationHeaderName}; + + private static final String coveredHeaderFormatted; + private static final String coveredHeaderGetFormatted; + + static { + coveredHeaderFormatted = formatCoveredHeaders(coveredHeaders); + coveredHeaderGetFormatted = formatCoveredHeaders(coveredHeadersGet); + } + + private final String keyId; + + public RequestSigningAuthenticator(String keyId) { + this.keyId = keyId; + } + + @Override + public final StripeRequest authenticate(StripeRequest request) throws AuthenticationException { + if (request.content() != null) { + request = + request.withAdditionalHeader( + contentDigestHeaderName, calculateDigestHeader(request.content())); + } + + final Long created = this.currentTimeInSecondsGetter.getCurrentTimeInSeconds(); + request = + request + .withAdditionalHeader(authorizationHeaderName, String.format("STRIPE-V2-SIG %s", keyId)) + .withAdditionalHeader( + signatureInputHeaderName, + String.format("sig1=%s", calculateSignatureInput(request.method(), created))); + + final byte[] signatureBase = calculateSignatureBase(request, created); + String signature; + + try { + signature = Base64.getEncoder().encodeToString(sign(signatureBase)); + } catch (GeneralSecurityException e) { + throw new AuthenticationException("Error calculating request signature.", null, null, 0, e); + } + request = + request.withAdditionalHeader(signatureHeaderName, String.format("sig1=:%s:", signature)); + + return request; + } + + public abstract byte[] sign(byte[] signatureBase) throws GeneralSecurityException; + + RequestSigningAuthenticator withCurrentTimeInSecondsGetter(CurrentTimeInSecondsGetter getter) { + this.currentTimeInSecondsGetter = getter; + return this; + } + + private String calculateDigestHeader(HttpContent content) { + MessageDigest messageDigest; + try { + messageDigest = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException( + "Error calculating request digest: your Java installation does not provide the SHA-256 digest algorithm, which is necessary for sending secure requests to Stripe.", + e); + } + + String digest = + Base64.getEncoder().encodeToString(messageDigest.digest(content.byteArrayContent())); + return String.format("sha-256=:%s:", digest); + } + + private byte[] calculateSignatureBase(StripeRequest request, Long created) { + StringBuilder stringBuilder = new StringBuilder(); + String[] headers = + request.method() == ApiResource.RequestMethod.GET ? coveredHeadersGet : coveredHeaders; + for (String header : headers) { + List values = request.headers().allValues(header); + + stringBuilder.append('"').append(header.toLowerCase()).append("\": "); + boolean firstValue = true; + for (String value : values) { + if (firstValue) { + firstValue = false; + } else { + stringBuilder.append(","); + } + stringBuilder.append(value); + } + + stringBuilder.append('\n'); + } + + stringBuilder.append("\"@signature-params\": "); + appendSignatureInput(stringBuilder, request.method(), created); + + return stringBuilder.toString().getBytes(StandardCharsets.UTF_8); + } + + private String calculateSignatureInput(ApiResource.RequestMethod method, Long created) { + StringBuilder stringBuilder = new StringBuilder(); + appendSignatureInput(stringBuilder, method, created); + return stringBuilder.toString(); + } + + private void appendSignatureInput( + StringBuilder stringBuilder, ApiResource.RequestMethod method, Long created) { + stringBuilder + .append( + method == ApiResource.RequestMethod.GET + ? coveredHeaderGetFormatted + : coveredHeaderFormatted) + .append(";created=") + .append(created); + } + + private static String formatCoveredHeaders(String[] headers) { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append('('); + boolean first = true; + for (String header : headers) { + if (first) { + first = false; + } else { + stringBuilder.append(' '); + } + stringBuilder.append('"').append(header.toLowerCase()).append('"'); + } + stringBuilder.append(')'); + return stringBuilder.toString(); + } +} diff --git a/src/main/java/com/stripe/net/StripeCollectionItemTypeSettingFactory.java b/src/main/java/com/stripe/net/StripeCollectionItemTypeSettingFactory.java index 8101ceb2f06..c881215e1cb 100644 --- a/src/main/java/com/stripe/net/StripeCollectionItemTypeSettingFactory.java +++ b/src/main/java/com/stripe/net/StripeCollectionItemTypeSettingFactory.java @@ -25,6 +25,8 @@ public T read(JsonReader in) throws IOException { T obj = delegate.read(in); if (obj instanceof StripeCollectionInterface) { ((StripeCollectionInterface) obj).setPageTypeToken(type.getType()); + } else if (obj instanceof com.stripe.model.v2.StripeCollection) { + ((com.stripe.model.v2.StripeCollection) obj).setPageTypeToken(type.getType()); } return obj; } diff --git a/src/main/java/com/stripe/net/StripeRequest.java b/src/main/java/com/stripe/net/StripeRequest.java index 53acfea729a..4afd391f9c6 100644 --- a/src/main/java/com/stripe/net/StripeRequest.java +++ b/src/main/java/com/stripe/net/StripeRequest.java @@ -4,15 +4,9 @@ import com.stripe.exception.ApiConnectionException; import com.stripe.exception.AuthenticationException; import com.stripe.exception.StripeException; -import com.stripe.util.StringUtils; import java.io.IOException; import java.net.URL; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.util.*; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Value; @@ -56,23 +50,26 @@ public class StripeRequest { * * @param method the HTTP method * @param url the URL of the request + * @param content the body of the request * @param params the parameters of the request * @param options the special modifiers of the request * @throws StripeException if the request cannot be initialized for any reason */ - public StripeRequest( + private StripeRequest( ApiResource.RequestMethod method, String url, + HttpContent content, Map params, - RequestOptions options) + RequestOptions options, + ApiMode apiMode) throws StripeException { try { + this.content = content; this.params = (params != null) ? Collections.unmodifiableMap(params) : null; this.options = (options != null) ? options : RequestOptions.getDefault(); this.method = method; - this.url = buildURL(method, url, params); - this.content = buildContent(method, params); - this.headers = buildHeaders(method, this.options); + this.url = buildURL(method, url, params, apiMode); + this.headers = buildHeaders(method, this.options, this.content, apiMode); } catch (IOException e) { throw new ApiConnectionException( String.format( @@ -85,6 +82,119 @@ public StripeRequest( } } + /** + * Initializes a new instance of the {@link StripeRequest} class. + * + * @param method the HTTP method + * @param url the URL of the request + * @param params the parameters of the request + * @param options the special modifiers of the request + * @param apiMode version of the API + * @throws StripeException if the request cannot be initialized for any reason + */ + StripeRequest( + ApiResource.RequestMethod method, + String url, + Map params, + RequestOptions options, + ApiMode apiMode) + throws StripeException { + try { + this.params = (params != null) ? Collections.unmodifiableMap(params) : null; + this.options = options; + this.method = method; + this.url = buildURL(method, url, params, apiMode); + this.content = buildContent(method, params, apiMode); + this.headers = buildHeaders(method, this.options, this.content, apiMode); + } catch (IOException e) { + throw new ApiConnectionException( + String.format( + "IOException during API request to Stripe (%s): %s " + + "Please check your internet connection and try again. If this problem persists," + + "you should check Stripe's service status at https://twitter.com/stripestatus," + + " or let us know at support@stripe.com.", + Stripe.getApiBase(), e.getMessage()), + e); + } + } + + /** + * Initializes a new instance of the {@link StripeRequest} class. + * + * @param method the HTTP method + * @param url the URL of the request + * @param params the parameters of the request + * @param options the special modifiers of the request + * @throws StripeException if the request cannot be initialized for any reason + */ + public static StripeRequest create( + ApiResource.RequestMethod method, + String url, + Map params, + RequestOptions options, + ApiMode apiMode) + throws StripeException { + if (options == null) { + throw new IllegalArgumentException("options parameter should not be null"); + } + + StripeRequest request = new StripeRequest(method, url, params, options, apiMode); + Authenticator authenticator = options.getAuthenticator(); + + if (authenticator == null) { + throw new AuthenticationException( + "No API key provided. Set your API key using `Stripe.apiKey = \"\"`. You can " + + "generate API keys from the Stripe Dashboard. See " + + "https://stripe.com/docs/api/authentication for details or contact support at " + + "https://support.stripe.com/email if you have any questions.", + null, + null, + 0); + } + + request = request.options().getAuthenticator().authenticate(request); + + return request; + } + + /** + * Initializes a new instance of the {@link StripeRequest} class. + * + * @param method the HTTP method + * @param url the URL of the request + * @param content the body of the request + * @param options the special modifiers of the request + * @throws StripeException if the request cannot be initialized for any reason + */ + public static StripeRequest createWithStringContent( + ApiResource.RequestMethod method, + String url, + String content, + RequestOptions options, + ApiMode apiMode) + throws StripeException { + StripeRequest request = + new StripeRequest( + method, url, buildContentFromString(method, content, apiMode), null, options, apiMode); + + Authenticator authenticator = options.getAuthenticator(); + + if (authenticator == null) { + throw new AuthenticationException( + "No API key provided. Set your API key using `Stripe.apiKey = \"\"`. You can " + + "generate API keys from the Stripe Dashboard. See " + + "https://stripe.com/docs/api/authentication for details or contact support at " + + "https://support.stripe.com/email if you have any questions.", + null, + null, + 0); + } + + request = request.options().getAuthenticator().authenticate(request); + + return request; + } + /** * Returns a new {@link StripeRequest} instance with an additional header. * @@ -103,7 +213,7 @@ public StripeRequest withAdditionalHeader(String name, String value) { } private static URL buildURL( - ApiResource.RequestMethod method, String spec, Map params) + ApiResource.RequestMethod method, String spec, Map params, ApiMode apiMode) throws IOException { StringBuilder sb = new StringBuilder(); @@ -113,7 +223,8 @@ private static URL buildURL( String specQueryString = specUrl.getQuery(); if ((method != ApiResource.RequestMethod.POST) && (params != null)) { - String queryString = FormEncoder.createQueryString(params); + String queryString = + FormEncoder.createQueryString(params, apiMode == ApiMode.V2 ? true : false); if (queryString != null && !queryString.isEmpty()) { if (specQueryString != null && !specQueryString.isEmpty()) { @@ -129,16 +240,55 @@ private static URL buildURL( } private static HttpContent buildContent( - ApiResource.RequestMethod method, Map params) throws IOException { + ApiResource.RequestMethod method, Map params, ApiMode apiMode) + throws IOException { if (method != ApiResource.RequestMethod.POST) { return null; } + if (apiMode == ApiMode.V2) { + return JsonEncoder.createHttpContent(params); + } + return FormEncoder.createHttpContent(params); } - private static HttpHeaders buildHeaders(ApiResource.RequestMethod method, RequestOptions options) - throws AuthenticationException { + private static HttpContent buildContentFromString( + ApiResource.RequestMethod method, String content, ApiMode apiMode) + throws ApiConnectionException { + if (method != ApiResource.RequestMethod.POST) { + return null; + } + + if (apiMode == ApiMode.V2) { + return HttpContent.buildJsonContent(content); + } + + HttpContent httpContent = null; + try { + httpContent = HttpContent.buildFormURLEncodedContent(content); + } catch (IOException e) { + handleIOException(e); + } + return httpContent; + } + + private static void handleIOException(IOException e) throws ApiConnectionException { + throw new ApiConnectionException( + String.format( + "IOException during API request to Stripe (%s): %s " + + "Please check your internet connection and try again. If this problem persists," + + "you should check Stripe's service status at https://twitter.com/stripestatus," + + " or let us know at support@stripe.com.", + Stripe.getApiBase(), e.getMessage()), + e); + } + + private static HttpHeaders buildHeaders( + ApiResource.RequestMethod method, + RequestOptions options, + HttpContent content, + ApiMode apiMode) { Map> headerMap = new HashMap>(); // Accept @@ -147,47 +297,25 @@ private static HttpHeaders buildHeaders(ApiResource.RequestMethod method, Reques // Accept-Charset headerMap.put("Accept-Charset", Arrays.asList(ApiResource.CHARSET.name())); - // Authorization - String apiKey = options.getApiKey(); - if (apiKey == null) { - throw new AuthenticationException( - "No API key provided. Set your API key using `Stripe.apiKey = \"\"`. You can " - + "generate API keys from the Stripe Dashboard. See " - + "https://stripe.com/docs/api/authentication for details or contact support at " - + "https://support.stripe.com/email if you have any questions.", - null, - null, - 0); - } else if (apiKey.isEmpty()) { - throw new AuthenticationException( - "Your API key is invalid, as it is an empty string. You can double-check your API key " - + "from the Stripe Dashboard. See " - + "https://stripe.com/docs/api/authentication for details or contact support at " - + "https://support.stripe.com/email if you have any questions.", - null, - null, - 0); - } else if (StringUtils.containsWhitespace(apiKey)) { - throw new AuthenticationException( - "Your API key is invalid, as it contains whitespace. You can double-check your API key " - + "from the Stripe Dashboard. See " - + "https://stripe.com/docs/api/authentication for details or contact support at " - + "https://support.stripe.com/email if you have any questions.", - null, - null, - 0); - } - headerMap.put("Authorization", Arrays.asList(String.format("Bearer %s", apiKey))); - // Stripe-Version if (RequestOptions.unsafeGetStripeVersionOverride(options) != null) { headerMap.put( "Stripe-Version", Arrays.asList(RequestOptions.unsafeGetStripeVersionOverride(options))); } else if (options.getStripeVersion() != null) { headerMap.put("Stripe-Version", Arrays.asList(options.getStripeVersion())); + } + + if (apiMode == ApiMode.V1) { + if (options.getStripeContext() != null) { + throw new UnsupportedOperationException("Context is not supported in V1 APIs"); + } } else { - throw new IllegalStateException( - "Either `stripeVersion` or `stripeVersionOverride` value must be set."); + if (options.getStripeContext() != null) { + headerMap.put("Stripe-Context", Arrays.asList(options.getStripeContext())); + } + if (content != null) { + headerMap.put("Content-Type", Arrays.asList(content.contentType())); + } } // Stripe-Account @@ -198,7 +326,8 @@ private static HttpHeaders buildHeaders(ApiResource.RequestMethod method, Reques // Idempotency-Key if (options.getIdempotencyKey() != null) { headerMap.put("Idempotency-Key", Arrays.asList(options.getIdempotencyKey())); - } else if (method == ApiResource.RequestMethod.POST) { + } else if (method == ApiResource.RequestMethod.POST + || (apiMode == ApiMode.V2 && method == ApiResource.RequestMethod.DELETE)) { headerMap.put("Idempotency-Key", Arrays.asList(UUID.randomUUID().toString())); } diff --git a/src/main/java/com/stripe/net/StripeResponseGetter.java b/src/main/java/com/stripe/net/StripeResponseGetter.java index 5b27a9862c6..cb6b54cf10d 100644 --- a/src/main/java/com/stripe/net/StripeResponseGetter.java +++ b/src/main/java/com/stripe/net/StripeResponseGetter.java @@ -55,6 +55,11 @@ default InputStream requestStream(ApiRequest request) throws StripeException { request.getApiMode()); }; + default StripeResponse rawRequest(RawApiRequest request) throws StripeException { + throw new UnsupportedOperationException( + "rawRequest is unimplemented for this StripeResponseGetter"); + }; + /** * This method should e.g. throws an ApiKeyMissingError if a proper API Key cannot be determined * by the ResponseGetter or from the RequestOptions passed in. diff --git a/src/main/java/com/stripe/net/StripeResponseGetterOptions.java b/src/main/java/com/stripe/net/StripeResponseGetterOptions.java index 8aa2fbccb4a..e2121017c1d 100644 --- a/src/main/java/com/stripe/net/StripeResponseGetterOptions.java +++ b/src/main/java/com/stripe/net/StripeResponseGetterOptions.java @@ -7,7 +7,7 @@ public abstract class StripeResponseGetterOptions { // When adding settings here keep them in sync with settings in RequestOptions and // in the RequestOptions.merge method - public abstract String getApiKey(); + public abstract Authenticator getAuthenticator(); public abstract String getClientId(); @@ -25,5 +25,9 @@ public abstract class StripeResponseGetterOptions { public abstract String getConnectBase(); + public abstract String getMeterEventsBase(); + public abstract int getReadTimeout(); + + public abstract String getStripeContext(); } diff --git a/src/main/java/com/stripe/net/Webhook.java b/src/main/java/com/stripe/net/Webhook.java index 09faafc4330..7b5a34febf2 100644 --- a/src/main/java/com/stripe/net/Webhook.java +++ b/src/main/java/com/stripe/net/Webhook.java @@ -14,7 +14,7 @@ import javax.crypto.spec.SecretKeySpec; public final class Webhook { - private static final long DEFAULT_TOLERANCE = 300; + public static final long DEFAULT_TOLERANCE = 300; /** * Returns an Event instance using the provided JSON payload. Throws a JsonSyntaxException if the diff --git a/src/main/java/com/stripe/param/ProductCreateParams.java b/src/main/java/com/stripe/param/ProductCreateParams.java index c1b26d14a1d..6ab9a8a8b8f 100644 --- a/src/main/java/com/stripe/param/ProductCreateParams.java +++ b/src/main/java/com/stripe/param/ProductCreateParams.java @@ -466,6 +466,13 @@ public static class DefaultPriceData { @SerializedName("currency_options") Map currencyOptions; + /** + * When set, provides configuration for the amount to be adjusted by the customer during + * Checkout Sessions and Payment Links. + */ + @SerializedName("custom_unit_amount") + CustomUnitAmount customUnitAmount; + /** * Map of extra parameters for custom features not available in this client library. The content * in this map is not serialized under this field's {@code @SerializedName} value. Instead, each @@ -492,7 +499,8 @@ public static class DefaultPriceData { /** * A positive integer in cents (or local equivalent) (or 0 for a free price) representing how - * much to charge. One of {@code unit_amount} or {@code unit_amount_decimal} is required. + * much to charge. One of {@code unit_amount}, {@code unit_amount_decimal}, or {@code + * custom_unit_amount} is required. */ @SerializedName("unit_amount") Long unitAmount; @@ -508,6 +516,7 @@ public static class DefaultPriceData { private DefaultPriceData( String currency, Map currencyOptions, + CustomUnitAmount customUnitAmount, Map extraParams, Recurring recurring, TaxBehavior taxBehavior, @@ -515,6 +524,7 @@ private DefaultPriceData( BigDecimal unitAmountDecimal) { this.currency = currency; this.currencyOptions = currencyOptions; + this.customUnitAmount = customUnitAmount; this.extraParams = extraParams; this.recurring = recurring; this.taxBehavior = taxBehavior; @@ -531,6 +541,8 @@ public static class Builder { private Map currencyOptions; + private CustomUnitAmount customUnitAmount; + private Map extraParams; private Recurring recurring; @@ -546,6 +558,7 @@ public ProductCreateParams.DefaultPriceData build() { return new ProductCreateParams.DefaultPriceData( this.currency, this.currencyOptions, + this.customUnitAmount, this.extraParams, this.recurring, this.taxBehavior, @@ -593,6 +606,16 @@ public Builder putAllCurrencyOption( return this; } + /** + * When set, provides configuration for the amount to be adjusted by the customer during + * Checkout Sessions and Payment Links. + */ + public Builder setCustomUnitAmount( + ProductCreateParams.DefaultPriceData.CustomUnitAmount customUnitAmount) { + this.customUnitAmount = customUnitAmount; + return this; + } + /** * Add a key/value pair to `extraParams` map. A map is initialized for the first `put/putAll` * call, and subsequent calls add additional key/value pairs to the original map. See {@link @@ -642,7 +665,8 @@ public Builder setTaxBehavior(ProductCreateParams.DefaultPriceData.TaxBehavior t /** * A positive integer in cents (or local equivalent) (or 0 for a free price) representing how - * much to charge. One of {@code unit_amount} or {@code unit_amount_decimal} is required. + * much to charge. One of {@code unit_amount}, {@code unit_amount_decimal}, or {@code + * custom_unit_amount} is required. */ public Builder setUnitAmount(Long unitAmount) { this.unitAmount = unitAmount; @@ -1190,6 +1214,133 @@ public enum TaxBehavior implements ApiRequestParams.EnumParam { } } + @Getter + public static class CustomUnitAmount { + /** + * Required. Pass in {@code true} to enable {@code custom_unit_amount}, + * otherwise omit {@code custom_unit_amount}. + */ + @SerializedName("enabled") + Boolean enabled; + + /** + * Map of extra parameters for custom features not available in this client library. The + * content in this map is not serialized under this field's {@code @SerializedName} value. + * Instead, each key/value pair is serialized as if the key is a root-level field (serialized) + * name in this param object. Effectively, this map is flattened to its parent instance. + */ + @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) + Map extraParams; + + /** The maximum unit amount the customer can specify for this item. */ + @SerializedName("maximum") + Long maximum; + + /** + * The minimum unit amount the customer can specify for this item. Must be at least the + * minimum charge amount. + */ + @SerializedName("minimum") + Long minimum; + + /** The starting unit amount which can be updated by the customer. */ + @SerializedName("preset") + Long preset; + + private CustomUnitAmount( + Boolean enabled, + Map extraParams, + Long maximum, + Long minimum, + Long preset) { + this.enabled = enabled; + this.extraParams = extraParams; + this.maximum = maximum; + this.minimum = minimum; + this.preset = preset; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private Boolean enabled; + + private Map extraParams; + + private Long maximum; + + private Long minimum; + + private Long preset; + + /** Finalize and obtain parameter instance from this builder. */ + public ProductCreateParams.DefaultPriceData.CustomUnitAmount build() { + return new ProductCreateParams.DefaultPriceData.CustomUnitAmount( + this.enabled, this.extraParams, this.maximum, this.minimum, this.preset); + } + + /** + * Required. Pass in {@code true} to enable {@code custom_unit_amount}, + * otherwise omit {@code custom_unit_amount}. + */ + public Builder setEnabled(Boolean enabled) { + this.enabled = enabled; + return this; + } + + /** + * Add a key/value pair to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original + * map. See {@link ProductCreateParams.DefaultPriceData.CustomUnitAmount#extraParams} for + * the field documentation. + */ + public Builder putExtraParam(String key, Object value) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original + * map. See {@link ProductCreateParams.DefaultPriceData.CustomUnitAmount#extraParams} for + * the field documentation. + */ + public Builder putAllExtraParam(Map map) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.putAll(map); + return this; + } + + /** The maximum unit amount the customer can specify for this item. */ + public Builder setMaximum(Long maximum) { + this.maximum = maximum; + return this; + } + + /** + * The minimum unit amount the customer can specify for this item. Must be at least the + * minimum charge amount. + */ + public Builder setMinimum(Long minimum) { + this.minimum = minimum; + return this; + } + + /** The starting unit amount which can be updated by the customer. */ + public Builder setPreset(Long preset) { + this.preset = preset; + return this; + } + } + } + @Getter public static class Recurring { /** diff --git a/src/main/java/com/stripe/param/PromotionCodeCreateParams.java b/src/main/java/com/stripe/param/PromotionCodeCreateParams.java index db93888390e..b103e7abf89 100644 --- a/src/main/java/com/stripe/param/PromotionCodeCreateParams.java +++ b/src/main/java/com/stripe/param/PromotionCodeCreateParams.java @@ -17,7 +17,10 @@ public class PromotionCodeCreateParams extends ApiRequestParams { /** * The customer-facing code. Regardless of case, this code must be unique across all active - * promotion codes for a specific customer. If left blank, we will generate one automatically. + * promotion codes for a specific customer. Valid characters are lower case letters (a-z), upper + * case letters (A-Z), and digits (0-9). + * + *

If left blank, we will generate one automatically. */ @SerializedName("code") String code; @@ -145,7 +148,10 @@ public Builder setActive(Boolean active) { /** * The customer-facing code. Regardless of case, this code must be unique across all active - * promotion codes for a specific customer. If left blank, we will generate one automatically. + * promotion codes for a specific customer. Valid characters are lower case letters (a-z), upper + * case letters (A-Z), and digits (0-9). + * + *

If left blank, we will generate one automatically. */ public Builder setCode(String code) { this.code = code; diff --git a/src/main/java/com/stripe/param/WebhookEndpointCreateParams.java b/src/main/java/com/stripe/param/WebhookEndpointCreateParams.java index c58bb754745..9b63c4a34bc 100644 --- a/src/main/java/com/stripe/param/WebhookEndpointCreateParams.java +++ b/src/main/java/com/stripe/param/WebhookEndpointCreateParams.java @@ -586,7 +586,10 @@ public enum ApiVersion implements ApiRequestParams.EnumParam { VERSION_2024_04_10("2024-04-10"), @SerializedName("2024-06-20") - VERSION_2024_06_20("2024-06-20"); + VERSION_2024_06_20("2024-06-20"), + + @SerializedName("2024-09-30.acacia") + VERSION_2024_09_30_ACACIA("2024-09-30.acacia"); @Getter(onMethod_ = {@Override}) private final String value; diff --git a/src/main/java/com/stripe/param/billing/AlertCreateParams.java b/src/main/java/com/stripe/param/billing/AlertCreateParams.java index af8441deb1c..c039cf504bb 100644 --- a/src/main/java/com/stripe/param/billing/AlertCreateParams.java +++ b/src/main/java/com/stripe/param/billing/AlertCreateParams.java @@ -28,31 +28,25 @@ public class AlertCreateParams extends ApiRequestParams { @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) Map extraParams; - /** Filters to limit the scope of an alert. */ - @SerializedName("filter") - Filter filter; - /** Required. The title of the alert. */ @SerializedName("title") String title; /** The configuration of the usage threshold. */ - @SerializedName("usage_threshold_config") - UsageThresholdConfig usageThresholdConfig; + @SerializedName("usage_threshold") + UsageThreshold usageThreshold; private AlertCreateParams( AlertType alertType, List expand, Map extraParams, - Filter filter, String title, - UsageThresholdConfig usageThresholdConfig) { + UsageThreshold usageThreshold) { this.alertType = alertType; this.expand = expand; this.extraParams = extraParams; - this.filter = filter; this.title = title; - this.usageThresholdConfig = usageThresholdConfig; + this.usageThreshold = usageThreshold; } public static Builder builder() { @@ -66,21 +60,14 @@ public static class Builder { private Map extraParams; - private Filter filter; - private String title; - private UsageThresholdConfig usageThresholdConfig; + private UsageThreshold usageThreshold; /** Finalize and obtain parameter instance from this builder. */ public AlertCreateParams build() { return new AlertCreateParams( - this.alertType, - this.expand, - this.extraParams, - this.filter, - this.title, - this.usageThresholdConfig); + this.alertType, this.expand, this.extraParams, this.title, this.usageThreshold); } /** Required. The type of alert to create. */ @@ -141,12 +128,6 @@ public Builder putAllExtraParam(Map map) { return this; } - /** Filters to limit the scope of an alert. */ - public Builder setFilter(AlertCreateParams.Filter filter) { - this.filter = filter; - return this; - } - /** Required. The title of the alert. */ public Builder setTitle(String title) { this.title = title; @@ -154,19 +135,14 @@ public Builder setTitle(String title) { } /** The configuration of the usage threshold. */ - public Builder setUsageThresholdConfig( - AlertCreateParams.UsageThresholdConfig usageThresholdConfig) { - this.usageThresholdConfig = usageThresholdConfig; + public Builder setUsageThreshold(AlertCreateParams.UsageThreshold usageThreshold) { + this.usageThreshold = usageThreshold; return this; } } @Getter - public static class Filter { - /** Limit the scope to this alert only to this customer. */ - @SerializedName("customer") - String customer; - + public static class UsageThreshold { /** * Map of extra parameters for custom features not available in this client library. The content * in this map is not serialized under this field's {@code @SerializedName} value. Instead, each @@ -176,100 +152,12 @@ public static class Filter { @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) Map extraParams; - /** Limit the scope of this rated usage alert to this subscription. */ - @SerializedName("subscription") - String subscription; - - /** Limit the scope of this rated usage alert to this subscription item. */ - @SerializedName("subscription_item") - String subscriptionItem; - - private Filter( - String customer, - Map extraParams, - String subscription, - String subscriptionItem) { - this.customer = customer; - this.extraParams = extraParams; - this.subscription = subscription; - this.subscriptionItem = subscriptionItem; - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - private String customer; - - private Map extraParams; - - private String subscription; - - private String subscriptionItem; - - /** Finalize and obtain parameter instance from this builder. */ - public AlertCreateParams.Filter build() { - return new AlertCreateParams.Filter( - this.customer, this.extraParams, this.subscription, this.subscriptionItem); - } - - /** Limit the scope to this alert only to this customer. */ - public Builder setCustomer(String customer) { - this.customer = customer; - return this; - } - - /** - * Add a key/value pair to `extraParams` map. A map is initialized for the first `put/putAll` - * call, and subsequent calls add additional key/value pairs to the original map. See {@link - * AlertCreateParams.Filter#extraParams} for the field documentation. - */ - public Builder putExtraParam(String key, Object value) { - if (this.extraParams == null) { - this.extraParams = new HashMap<>(); - } - this.extraParams.put(key, value); - return this; - } - - /** - * Add all map key/value pairs to `extraParams` map. A map is initialized for the first - * `put/putAll` call, and subsequent calls add additional key/value pairs to the original map. - * See {@link AlertCreateParams.Filter#extraParams} for the field documentation. - */ - public Builder putAllExtraParam(Map map) { - if (this.extraParams == null) { - this.extraParams = new HashMap<>(); - } - this.extraParams.putAll(map); - return this; - } - - /** Limit the scope of this rated usage alert to this subscription. */ - public Builder setSubscription(String subscription) { - this.subscription = subscription; - return this; - } - - /** Limit the scope of this rated usage alert to this subscription item. */ - public Builder setSubscriptionItem(String subscriptionItem) { - this.subscriptionItem = subscriptionItem; - return this; - } - } - } - - @Getter - public static class UsageThresholdConfig { /** - * Map of extra parameters for custom features not available in this client library. The content - * in this map is not serialized under this field's {@code @SerializedName} value. Instead, each - * key/value pair is serialized as if the key is a root-level field (serialized) name in this - * param object. Effectively, this map is flattened to its parent instance. + * The filters allows limiting the scope of this usage alert. You can only specify up to one + * filter at this time. */ - @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) - Map extraParams; + @SerializedName("filters") + List filters; /** Required. Defines at which value the alert will fire. */ @SerializedName("gte") @@ -289,9 +177,14 @@ public static class UsageThresholdConfig { @SerializedName("recurrence") Recurrence recurrence; - private UsageThresholdConfig( - Map extraParams, Long gte, String meter, Recurrence recurrence) { + private UsageThreshold( + Map extraParams, + List filters, + Long gte, + String meter, + Recurrence recurrence) { this.extraParams = extraParams; + this.filters = filters; this.gte = gte; this.meter = meter; this.recurrence = recurrence; @@ -304,6 +197,8 @@ public static Builder builder() { public static class Builder { private Map extraParams; + private List filters; + private Long gte; private String meter; @@ -311,15 +206,15 @@ public static class Builder { private Recurrence recurrence; /** Finalize and obtain parameter instance from this builder. */ - public AlertCreateParams.UsageThresholdConfig build() { - return new AlertCreateParams.UsageThresholdConfig( - this.extraParams, this.gte, this.meter, this.recurrence); + public AlertCreateParams.UsageThreshold build() { + return new AlertCreateParams.UsageThreshold( + this.extraParams, this.filters, this.gte, this.meter, this.recurrence); } /** * Add a key/value pair to `extraParams` map. A map is initialized for the first `put/putAll` * call, and subsequent calls add additional key/value pairs to the original map. See {@link - * AlertCreateParams.UsageThresholdConfig#extraParams} for the field documentation. + * AlertCreateParams.UsageThreshold#extraParams} for the field documentation. */ public Builder putExtraParam(String key, Object value) { if (this.extraParams == null) { @@ -332,7 +227,7 @@ public Builder putExtraParam(String key, Object value) { /** * Add all map key/value pairs to `extraParams` map. A map is initialized for the first * `put/putAll` call, and subsequent calls add additional key/value pairs to the original map. - * See {@link AlertCreateParams.UsageThresholdConfig#extraParams} for the field documentation. + * See {@link AlertCreateParams.UsageThreshold#extraParams} for the field documentation. */ public Builder putAllExtraParam(Map map) { if (this.extraParams == null) { @@ -342,6 +237,32 @@ public Builder putAllExtraParam(Map map) { return this; } + /** + * Add an element to `filters` list. A list is initialized for the first `add/addAll` call, + * and subsequent calls adds additional elements to the original list. See {@link + * AlertCreateParams.UsageThreshold#filters} for the field documentation. + */ + public Builder addFilter(AlertCreateParams.UsageThreshold.Filter element) { + if (this.filters == null) { + this.filters = new ArrayList<>(); + } + this.filters.add(element); + return this; + } + + /** + * Add all elements to `filters` list. A list is initialized for the first `add/addAll` call, + * and subsequent calls adds additional elements to the original list. See {@link + * AlertCreateParams.UsageThreshold#filters} for the field documentation. + */ + public Builder addAllFilter(List elements) { + if (this.filters == null) { + this.filters = new ArrayList<>(); + } + this.filters.addAll(elements); + return this; + } + /** Required. Defines at which value the alert will fire. */ public Builder setGte(Long gte) { this.gte = gte; @@ -361,12 +282,108 @@ public Builder setMeter(String meter) { * Required. Whether the alert should only fire only once, or once per * billing cycle. */ - public Builder setRecurrence(AlertCreateParams.UsageThresholdConfig.Recurrence recurrence) { + public Builder setRecurrence(AlertCreateParams.UsageThreshold.Recurrence recurrence) { this.recurrence = recurrence; return this; } } + @Getter + public static class Filter { + /** Limit the scope to this usage alert only to this customer. */ + @SerializedName("customer") + String customer; + + /** + * Map of extra parameters for custom features not available in this client library. The + * content in this map is not serialized under this field's {@code @SerializedName} value. + * Instead, each key/value pair is serialized as if the key is a root-level field (serialized) + * name in this param object. Effectively, this map is flattened to its parent instance. + */ + @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) + Map extraParams; + + /** Required. What type of filter is being applied to this usage alert. */ + @SerializedName("type") + Type type; + + private Filter(String customer, Map extraParams, Type type) { + this.customer = customer; + this.extraParams = extraParams; + this.type = type; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String customer; + + private Map extraParams; + + private Type type; + + /** Finalize and obtain parameter instance from this builder. */ + public AlertCreateParams.UsageThreshold.Filter build() { + return new AlertCreateParams.UsageThreshold.Filter( + this.customer, this.extraParams, this.type); + } + + /** Limit the scope to this usage alert only to this customer. */ + public Builder setCustomer(String customer) { + this.customer = customer; + return this; + } + + /** + * Add a key/value pair to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original + * map. See {@link AlertCreateParams.UsageThreshold.Filter#extraParams} for the field + * documentation. + */ + public Builder putExtraParam(String key, Object value) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original + * map. See {@link AlertCreateParams.UsageThreshold.Filter#extraParams} for the field + * documentation. + */ + public Builder putAllExtraParam(Map map) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.putAll(map); + return this; + } + + /** Required. What type of filter is being applied to this usage alert. */ + public Builder setType(AlertCreateParams.UsageThreshold.Filter.Type type) { + this.type = type; + return this; + } + } + + public enum Type implements ApiRequestParams.EnumParam { + @SerializedName("customer") + CUSTOMER("customer"); + + @Getter(onMethod_ = {@Override}) + private final String value; + + Type(String value) { + this.value = value; + } + } + } + public enum Recurrence implements ApiRequestParams.EnumParam { @SerializedName("one_time") ONE_TIME("one_time"); diff --git a/src/main/java/com/stripe/param/billing/CreditBalanceSummaryRetrieveParams.java b/src/main/java/com/stripe/param/billing/CreditBalanceSummaryRetrieveParams.java new file mode 100644 index 00000000000..3f9453ebe76 --- /dev/null +++ b/src/main/java/com/stripe/param/billing/CreditBalanceSummaryRetrieveParams.java @@ -0,0 +1,332 @@ +// File generated from our OpenAPI spec +package com.stripe.param.billing; + +import com.google.gson.annotations.SerializedName; +import com.stripe.net.ApiRequestParams; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.Getter; + +@Getter +public class CreditBalanceSummaryRetrieveParams extends ApiRequestParams { + /** Required. The customer for which to fetch credit balance summary. */ + @SerializedName("customer") + String customer; + + /** Specifies which fields in the response should be expanded. */ + @SerializedName("expand") + List expand; + + /** + * Map of extra parameters for custom features not available in this client library. The content + * in this map is not serialized under this field's {@code @SerializedName} value. Instead, each + * key/value pair is serialized as if the key is a root-level field (serialized) name in this + * param object. Effectively, this map is flattened to its parent instance. + */ + @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) + Map extraParams; + + /** Required. The filter criteria for the credit balance summary. */ + @SerializedName("filter") + Filter filter; + + private CreditBalanceSummaryRetrieveParams( + String customer, List expand, Map extraParams, Filter filter) { + this.customer = customer; + this.expand = expand; + this.extraParams = extraParams; + this.filter = filter; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String customer; + + private List expand; + + private Map extraParams; + + private Filter filter; + + /** Finalize and obtain parameter instance from this builder. */ + public CreditBalanceSummaryRetrieveParams build() { + return new CreditBalanceSummaryRetrieveParams( + this.customer, this.expand, this.extraParams, this.filter); + } + + /** Required. The customer for which to fetch credit balance summary. */ + public Builder setCustomer(String customer) { + this.customer = customer; + return this; + } + + /** + * Add an element to `expand` list. A list is initialized for the first `add/addAll` call, and + * subsequent calls adds additional elements to the original list. See {@link + * CreditBalanceSummaryRetrieveParams#expand} for the field documentation. + */ + public Builder addExpand(String element) { + if (this.expand == null) { + this.expand = new ArrayList<>(); + } + this.expand.add(element); + return this; + } + + /** + * Add all elements to `expand` list. A list is initialized for the first `add/addAll` call, and + * subsequent calls adds additional elements to the original list. See {@link + * CreditBalanceSummaryRetrieveParams#expand} for the field documentation. + */ + public Builder addAllExpand(List elements) { + if (this.expand == null) { + this.expand = new ArrayList<>(); + } + this.expand.addAll(elements); + return this; + } + + /** + * Add a key/value pair to `extraParams` map. A map is initialized for the first `put/putAll` + * call, and subsequent calls add additional key/value pairs to the original map. See {@link + * CreditBalanceSummaryRetrieveParams#extraParams} for the field documentation. + */ + public Builder putExtraParam(String key, Object value) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original map. + * See {@link CreditBalanceSummaryRetrieveParams#extraParams} for the field documentation. + */ + public Builder putAllExtraParam(Map map) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.putAll(map); + return this; + } + + /** Required. The filter criteria for the credit balance summary. */ + public Builder setFilter(CreditBalanceSummaryRetrieveParams.Filter filter) { + this.filter = filter; + return this; + } + } + + @Getter + public static class Filter { + /** The credit applicability scope for which to fetch balance summary. */ + @SerializedName("applicability_scope") + ApplicabilityScope applicabilityScope; + + /** The credit grant for which to fetch balance summary. */ + @SerializedName("credit_grant") + String creditGrant; + + /** + * Map of extra parameters for custom features not available in this client library. The content + * in this map is not serialized under this field's {@code @SerializedName} value. Instead, each + * key/value pair is serialized as if the key is a root-level field (serialized) name in this + * param object. Effectively, this map is flattened to its parent instance. + */ + @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) + Map extraParams; + + /** Required. Specify the type of this filter. */ + @SerializedName("type") + Type type; + + private Filter( + ApplicabilityScope applicabilityScope, + String creditGrant, + Map extraParams, + Type type) { + this.applicabilityScope = applicabilityScope; + this.creditGrant = creditGrant; + this.extraParams = extraParams; + this.type = type; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private ApplicabilityScope applicabilityScope; + + private String creditGrant; + + private Map extraParams; + + private Type type; + + /** Finalize and obtain parameter instance from this builder. */ + public CreditBalanceSummaryRetrieveParams.Filter build() { + return new CreditBalanceSummaryRetrieveParams.Filter( + this.applicabilityScope, this.creditGrant, this.extraParams, this.type); + } + + /** The credit applicability scope for which to fetch balance summary. */ + public Builder setApplicabilityScope( + CreditBalanceSummaryRetrieveParams.Filter.ApplicabilityScope applicabilityScope) { + this.applicabilityScope = applicabilityScope; + return this; + } + + /** The credit grant for which to fetch balance summary. */ + public Builder setCreditGrant(String creditGrant) { + this.creditGrant = creditGrant; + return this; + } + + /** + * Add a key/value pair to `extraParams` map. A map is initialized for the first `put/putAll` + * call, and subsequent calls add additional key/value pairs to the original map. See {@link + * CreditBalanceSummaryRetrieveParams.Filter#extraParams} for the field documentation. + */ + public Builder putExtraParam(String key, Object value) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original map. + * See {@link CreditBalanceSummaryRetrieveParams.Filter#extraParams} for the field + * documentation. + */ + public Builder putAllExtraParam(Map map) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.putAll(map); + return this; + } + + /** Required. Specify the type of this filter. */ + public Builder setType(CreditBalanceSummaryRetrieveParams.Filter.Type type) { + this.type = type; + return this; + } + } + + @Getter + public static class ApplicabilityScope { + /** + * Map of extra parameters for custom features not available in this client library. The + * content in this map is not serialized under this field's {@code @SerializedName} value. + * Instead, each key/value pair is serialized as if the key is a root-level field (serialized) + * name in this param object. Effectively, this map is flattened to its parent instance. + */ + @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) + Map extraParams; + + /** + * Required. The price type to which credit grants can apply to. We currently + * only support {@code metered} price type. + */ + @SerializedName("price_type") + PriceType priceType; + + private ApplicabilityScope(Map extraParams, PriceType priceType) { + this.extraParams = extraParams; + this.priceType = priceType; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private Map extraParams; + + private PriceType priceType; + + /** Finalize and obtain parameter instance from this builder. */ + public CreditBalanceSummaryRetrieveParams.Filter.ApplicabilityScope build() { + return new CreditBalanceSummaryRetrieveParams.Filter.ApplicabilityScope( + this.extraParams, this.priceType); + } + + /** + * Add a key/value pair to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original + * map. See {@link CreditBalanceSummaryRetrieveParams.Filter.ApplicabilityScope#extraParams} + * for the field documentation. + */ + public Builder putExtraParam(String key, Object value) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original + * map. See {@link CreditBalanceSummaryRetrieveParams.Filter.ApplicabilityScope#extraParams} + * for the field documentation. + */ + public Builder putAllExtraParam(Map map) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.putAll(map); + return this; + } + + /** + * Required. The price type to which credit grants can apply to. We + * currently only support {@code metered} price type. + */ + public Builder setPriceType( + CreditBalanceSummaryRetrieveParams.Filter.ApplicabilityScope.PriceType priceType) { + this.priceType = priceType; + return this; + } + } + + public enum PriceType implements ApiRequestParams.EnumParam { + @SerializedName("metered") + METERED("metered"); + + @Getter(onMethod_ = {@Override}) + private final String value; + + PriceType(String value) { + this.value = value; + } + } + } + + public enum Type implements ApiRequestParams.EnumParam { + @SerializedName("applicability_scope") + APPLICABILITY_SCOPE("applicability_scope"), + + @SerializedName("credit_grant") + CREDIT_GRANT("credit_grant"); + + @Getter(onMethod_ = {@Override}) + private final String value; + + Type(String value) { + this.value = value; + } + } + } +} diff --git a/src/main/java/com/stripe/param/billing/CreditBalanceTransactionListParams.java b/src/main/java/com/stripe/param/billing/CreditBalanceTransactionListParams.java new file mode 100644 index 00000000000..ed3811c27d1 --- /dev/null +++ b/src/main/java/com/stripe/param/billing/CreditBalanceTransactionListParams.java @@ -0,0 +1,203 @@ +// File generated from our OpenAPI spec +package com.stripe.param.billing; + +import com.google.gson.annotations.SerializedName; +import com.stripe.net.ApiRequestParams; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.Getter; + +@Getter +public class CreditBalanceTransactionListParams extends ApiRequestParams { + /** The credit grant for which to fetch credit balance transactions. */ + @SerializedName("credit_grant") + String creditGrant; + + /** Required. The customer for which to fetch credit balance transactions. */ + @SerializedName("customer") + String customer; + + /** + * A cursor for use in pagination. {@code ending_before} is an object ID that defines your place + * in the list. For instance, if you make a list request and receive 100 objects, starting with + * {@code obj_bar}, your subsequent call can include {@code ending_before=obj_bar} in order to + * fetch the previous page of the list. + */ + @SerializedName("ending_before") + String endingBefore; + + /** Specifies which fields in the response should be expanded. */ + @SerializedName("expand") + List expand; + + /** + * Map of extra parameters for custom features not available in this client library. The content + * in this map is not serialized under this field's {@code @SerializedName} value. Instead, each + * key/value pair is serialized as if the key is a root-level field (serialized) name in this + * param object. Effectively, this map is flattened to its parent instance. + */ + @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) + Map extraParams; + + /** + * A limit on the number of objects to be returned. Limit can range between 1 and 100, and the + * default is 10. + */ + @SerializedName("limit") + Long limit; + + /** + * A cursor for use in pagination. {@code starting_after} is an object ID that defines your place + * in the list. For instance, if you make a list request and receive 100 objects, ending with + * {@code obj_foo}, your subsequent call can include {@code starting_after=obj_foo} in order to + * fetch the next page of the list. + */ + @SerializedName("starting_after") + String startingAfter; + + private CreditBalanceTransactionListParams( + String creditGrant, + String customer, + String endingBefore, + List expand, + Map extraParams, + Long limit, + String startingAfter) { + this.creditGrant = creditGrant; + this.customer = customer; + this.endingBefore = endingBefore; + this.expand = expand; + this.extraParams = extraParams; + this.limit = limit; + this.startingAfter = startingAfter; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String creditGrant; + + private String customer; + + private String endingBefore; + + private List expand; + + private Map extraParams; + + private Long limit; + + private String startingAfter; + + /** Finalize and obtain parameter instance from this builder. */ + public CreditBalanceTransactionListParams build() { + return new CreditBalanceTransactionListParams( + this.creditGrant, + this.customer, + this.endingBefore, + this.expand, + this.extraParams, + this.limit, + this.startingAfter); + } + + /** The credit grant for which to fetch credit balance transactions. */ + public Builder setCreditGrant(String creditGrant) { + this.creditGrant = creditGrant; + return this; + } + + /** Required. The customer for which to fetch credit balance transactions. */ + public Builder setCustomer(String customer) { + this.customer = customer; + return this; + } + + /** + * A cursor for use in pagination. {@code ending_before} is an object ID that defines your place + * in the list. For instance, if you make a list request and receive 100 objects, starting with + * {@code obj_bar}, your subsequent call can include {@code ending_before=obj_bar} in order to + * fetch the previous page of the list. + */ + public Builder setEndingBefore(String endingBefore) { + this.endingBefore = endingBefore; + return this; + } + + /** + * Add an element to `expand` list. A list is initialized for the first `add/addAll` call, and + * subsequent calls adds additional elements to the original list. See {@link + * CreditBalanceTransactionListParams#expand} for the field documentation. + */ + public Builder addExpand(String element) { + if (this.expand == null) { + this.expand = new ArrayList<>(); + } + this.expand.add(element); + return this; + } + + /** + * Add all elements to `expand` list. A list is initialized for the first `add/addAll` call, and + * subsequent calls adds additional elements to the original list. See {@link + * CreditBalanceTransactionListParams#expand} for the field documentation. + */ + public Builder addAllExpand(List elements) { + if (this.expand == null) { + this.expand = new ArrayList<>(); + } + this.expand.addAll(elements); + return this; + } + + /** + * Add a key/value pair to `extraParams` map. A map is initialized for the first `put/putAll` + * call, and subsequent calls add additional key/value pairs to the original map. See {@link + * CreditBalanceTransactionListParams#extraParams} for the field documentation. + */ + public Builder putExtraParam(String key, Object value) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original map. + * See {@link CreditBalanceTransactionListParams#extraParams} for the field documentation. + */ + public Builder putAllExtraParam(Map map) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.putAll(map); + return this; + } + + /** + * A limit on the number of objects to be returned. Limit can range between 1 and 100, and the + * default is 10. + */ + public Builder setLimit(Long limit) { + this.limit = limit; + return this; + } + + /** + * A cursor for use in pagination. {@code starting_after} is an object ID that defines your + * place in the list. For instance, if you make a list request and receive 100 objects, ending + * with {@code obj_foo}, your subsequent call can include {@code starting_after=obj_foo} in + * order to fetch the next page of the list. + */ + public Builder setStartingAfter(String startingAfter) { + this.startingAfter = startingAfter; + return this; + } + } +} diff --git a/src/main/java/com/stripe/param/billing/CreditBalanceTransactionRetrieveParams.java b/src/main/java/com/stripe/param/billing/CreditBalanceTransactionRetrieveParams.java new file mode 100644 index 00000000000..2b6e700bc8e --- /dev/null +++ b/src/main/java/com/stripe/param/billing/CreditBalanceTransactionRetrieveParams.java @@ -0,0 +1,99 @@ +// File generated from our OpenAPI spec +package com.stripe.param.billing; + +import com.google.gson.annotations.SerializedName; +import com.stripe.net.ApiRequestParams; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.Getter; + +@Getter +public class CreditBalanceTransactionRetrieveParams extends ApiRequestParams { + /** Specifies which fields in the response should be expanded. */ + @SerializedName("expand") + List expand; + + /** + * Map of extra parameters for custom features not available in this client library. The content + * in this map is not serialized under this field's {@code @SerializedName} value. Instead, each + * key/value pair is serialized as if the key is a root-level field (serialized) name in this + * param object. Effectively, this map is flattened to its parent instance. + */ + @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) + Map extraParams; + + private CreditBalanceTransactionRetrieveParams( + List expand, Map extraParams) { + this.expand = expand; + this.extraParams = extraParams; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private List expand; + + private Map extraParams; + + /** Finalize and obtain parameter instance from this builder. */ + public CreditBalanceTransactionRetrieveParams build() { + return new CreditBalanceTransactionRetrieveParams(this.expand, this.extraParams); + } + + /** + * Add an element to `expand` list. A list is initialized for the first `add/addAll` call, and + * subsequent calls adds additional elements to the original list. See {@link + * CreditBalanceTransactionRetrieveParams#expand} for the field documentation. + */ + public Builder addExpand(String element) { + if (this.expand == null) { + this.expand = new ArrayList<>(); + } + this.expand.add(element); + return this; + } + + /** + * Add all elements to `expand` list. A list is initialized for the first `add/addAll` call, and + * subsequent calls adds additional elements to the original list. See {@link + * CreditBalanceTransactionRetrieveParams#expand} for the field documentation. + */ + public Builder addAllExpand(List elements) { + if (this.expand == null) { + this.expand = new ArrayList<>(); + } + this.expand.addAll(elements); + return this; + } + + /** + * Add a key/value pair to `extraParams` map. A map is initialized for the first `put/putAll` + * call, and subsequent calls add additional key/value pairs to the original map. See {@link + * CreditBalanceTransactionRetrieveParams#extraParams} for the field documentation. + */ + public Builder putExtraParam(String key, Object value) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original map. + * See {@link CreditBalanceTransactionRetrieveParams#extraParams} for the field documentation. + */ + public Builder putAllExtraParam(Map map) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.putAll(map); + return this; + } + } +} diff --git a/src/main/java/com/stripe/param/billing/CreditGrantCreateParams.java b/src/main/java/com/stripe/param/billing/CreditGrantCreateParams.java new file mode 100644 index 00000000000..6f1fd7e316a --- /dev/null +++ b/src/main/java/com/stripe/param/billing/CreditGrantCreateParams.java @@ -0,0 +1,620 @@ +// File generated from our OpenAPI spec +package com.stripe.param.billing; + +import com.google.gson.annotations.SerializedName; +import com.stripe.net.ApiRequestParams; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.Getter; + +@Getter +public class CreditGrantCreateParams extends ApiRequestParams { + /** Required. Amount of this credit grant. */ + @SerializedName("amount") + Amount amount; + + /** Required. Configuration specifying what this credit grant applies to. */ + @SerializedName("applicability_config") + ApplicabilityConfig applicabilityConfig; + + /** Required. The category of this credit grant. */ + @SerializedName("category") + Category category; + + /** Required. Id of the customer to whom the credit should be granted. */ + @SerializedName("customer") + String customer; + + /** + * The time when the credit becomes effective i.e when it is eligible to be used. Defaults to the + * current timestamp if not specified. + */ + @SerializedName("effective_at") + Long effectiveAt; + + /** Specifies which fields in the response should be expanded. */ + @SerializedName("expand") + List expand; + + /** The time when the credit will expire. If not specified, the credit will never expire. */ + @SerializedName("expires_at") + Long expiresAt; + + /** + * Map of extra parameters for custom features not available in this client library. The content + * in this map is not serialized under this field's {@code @SerializedName} value. Instead, each + * key/value pair is serialized as if the key is a root-level field (serialized) name in this + * param object. Effectively, this map is flattened to its parent instance. + */ + @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) + Map extraParams; + + /** + * Set of key-value pairs that you can attach to an object. This can be useful for storing + * additional information about the object (ex: cost basis) in a structured format. + */ + @SerializedName("metadata") + Map metadata; + + /** A descriptive name shown in dashboard and on invoices. */ + @SerializedName("name") + String name; + + private CreditGrantCreateParams( + Amount amount, + ApplicabilityConfig applicabilityConfig, + Category category, + String customer, + Long effectiveAt, + List expand, + Long expiresAt, + Map extraParams, + Map metadata, + String name) { + this.amount = amount; + this.applicabilityConfig = applicabilityConfig; + this.category = category; + this.customer = customer; + this.effectiveAt = effectiveAt; + this.expand = expand; + this.expiresAt = expiresAt; + this.extraParams = extraParams; + this.metadata = metadata; + this.name = name; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private Amount amount; + + private ApplicabilityConfig applicabilityConfig; + + private Category category; + + private String customer; + + private Long effectiveAt; + + private List expand; + + private Long expiresAt; + + private Map extraParams; + + private Map metadata; + + private String name; + + /** Finalize and obtain parameter instance from this builder. */ + public CreditGrantCreateParams build() { + return new CreditGrantCreateParams( + this.amount, + this.applicabilityConfig, + this.category, + this.customer, + this.effectiveAt, + this.expand, + this.expiresAt, + this.extraParams, + this.metadata, + this.name); + } + + /** Required. Amount of this credit grant. */ + public Builder setAmount(CreditGrantCreateParams.Amount amount) { + this.amount = amount; + return this; + } + + /** Required. Configuration specifying what this credit grant applies to. */ + public Builder setApplicabilityConfig( + CreditGrantCreateParams.ApplicabilityConfig applicabilityConfig) { + this.applicabilityConfig = applicabilityConfig; + return this; + } + + /** Required. The category of this credit grant. */ + public Builder setCategory(CreditGrantCreateParams.Category category) { + this.category = category; + return this; + } + + /** Required. Id of the customer to whom the credit should be granted. */ + public Builder setCustomer(String customer) { + this.customer = customer; + return this; + } + + /** + * The time when the credit becomes effective i.e when it is eligible to be used. Defaults to + * the current timestamp if not specified. + */ + public Builder setEffectiveAt(Long effectiveAt) { + this.effectiveAt = effectiveAt; + return this; + } + + /** + * Add an element to `expand` list. A list is initialized for the first `add/addAll` call, and + * subsequent calls adds additional elements to the original list. See {@link + * CreditGrantCreateParams#expand} for the field documentation. + */ + public Builder addExpand(String element) { + if (this.expand == null) { + this.expand = new ArrayList<>(); + } + this.expand.add(element); + return this; + } + + /** + * Add all elements to `expand` list. A list is initialized for the first `add/addAll` call, and + * subsequent calls adds additional elements to the original list. See {@link + * CreditGrantCreateParams#expand} for the field documentation. + */ + public Builder addAllExpand(List elements) { + if (this.expand == null) { + this.expand = new ArrayList<>(); + } + this.expand.addAll(elements); + return this; + } + + /** The time when the credit will expire. If not specified, the credit will never expire. */ + public Builder setExpiresAt(Long expiresAt) { + this.expiresAt = expiresAt; + return this; + } + + /** + * Add a key/value pair to `extraParams` map. A map is initialized for the first `put/putAll` + * call, and subsequent calls add additional key/value pairs to the original map. See {@link + * CreditGrantCreateParams#extraParams} for the field documentation. + */ + public Builder putExtraParam(String key, Object value) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original map. + * See {@link CreditGrantCreateParams#extraParams} for the field documentation. + */ + public Builder putAllExtraParam(Map map) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.putAll(map); + return this; + } + + /** + * Add a key/value pair to `metadata` map. A map is initialized for the first `put/putAll` call, + * and subsequent calls add additional key/value pairs to the original map. See {@link + * CreditGrantCreateParams#metadata} for the field documentation. + */ + public Builder putMetadata(String key, String value) { + if (this.metadata == null) { + this.metadata = new HashMap<>(); + } + this.metadata.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `metadata` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original map. + * See {@link CreditGrantCreateParams#metadata} for the field documentation. + */ + public Builder putAllMetadata(Map map) { + if (this.metadata == null) { + this.metadata = new HashMap<>(); + } + this.metadata.putAll(map); + return this; + } + + /** A descriptive name shown in dashboard and on invoices. */ + public Builder setName(String name) { + this.name = name; + return this; + } + } + + @Getter + public static class Amount { + /** + * Map of extra parameters for custom features not available in this client library. The content + * in this map is not serialized under this field's {@code @SerializedName} value. Instead, each + * key/value pair is serialized as if the key is a root-level field (serialized) name in this + * param object. Effectively, this map is flattened to its parent instance. + */ + @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) + Map extraParams; + + /** The monetary amount. */ + @SerializedName("monetary") + Monetary monetary; + + /** + * Required. Specify the type of this amount. We currently only support {@code + * monetary} credits. + */ + @SerializedName("type") + Type type; + + private Amount(Map extraParams, Monetary monetary, Type type) { + this.extraParams = extraParams; + this.monetary = monetary; + this.type = type; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private Map extraParams; + + private Monetary monetary; + + private Type type; + + /** Finalize and obtain parameter instance from this builder. */ + public CreditGrantCreateParams.Amount build() { + return new CreditGrantCreateParams.Amount(this.extraParams, this.monetary, this.type); + } + + /** + * Add a key/value pair to `extraParams` map. A map is initialized for the first `put/putAll` + * call, and subsequent calls add additional key/value pairs to the original map. See {@link + * CreditGrantCreateParams.Amount#extraParams} for the field documentation. + */ + public Builder putExtraParam(String key, Object value) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original map. + * See {@link CreditGrantCreateParams.Amount#extraParams} for the field documentation. + */ + public Builder putAllExtraParam(Map map) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.putAll(map); + return this; + } + + /** The monetary amount. */ + public Builder setMonetary(CreditGrantCreateParams.Amount.Monetary monetary) { + this.monetary = monetary; + return this; + } + + /** + * Required. Specify the type of this amount. We currently only support + * {@code monetary} credits. + */ + public Builder setType(CreditGrantCreateParams.Amount.Type type) { + this.type = type; + return this; + } + } + + @Getter + public static class Monetary { + /** + * Required. Three-letter ISO + * code for the currency of the {@code value} parameter. + */ + @SerializedName("currency") + String currency; + + /** + * Map of extra parameters for custom features not available in this client library. The + * content in this map is not serialized under this field's {@code @SerializedName} value. + * Instead, each key/value pair is serialized as if the key is a root-level field (serialized) + * name in this param object. Effectively, this map is flattened to its parent instance. + */ + @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) + Map extraParams; + + /** + * Required. A positive integer representing the amount of the credit grant. + */ + @SerializedName("value") + Long value; + + private Monetary(String currency, Map extraParams, Long value) { + this.currency = currency; + this.extraParams = extraParams; + this.value = value; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String currency; + + private Map extraParams; + + private Long value; + + /** Finalize and obtain parameter instance from this builder. */ + public CreditGrantCreateParams.Amount.Monetary build() { + return new CreditGrantCreateParams.Amount.Monetary( + this.currency, this.extraParams, this.value); + } + + /** + * Required. Three-letter ISO + * code for the currency of the {@code value} parameter. + */ + public Builder setCurrency(String currency) { + this.currency = currency; + return this; + } + + /** + * Add a key/value pair to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original + * map. See {@link CreditGrantCreateParams.Amount.Monetary#extraParams} for the field + * documentation. + */ + public Builder putExtraParam(String key, Object value) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original + * map. See {@link CreditGrantCreateParams.Amount.Monetary#extraParams} for the field + * documentation. + */ + public Builder putAllExtraParam(Map map) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.putAll(map); + return this; + } + + /** + * Required. A positive integer representing the amount of the credit + * grant. + */ + public Builder setValue(Long value) { + this.value = value; + return this; + } + } + } + + public enum Type implements ApiRequestParams.EnumParam { + @SerializedName("monetary") + MONETARY("monetary"); + + @Getter(onMethod_ = {@Override}) + private final String value; + + Type(String value) { + this.value = value; + } + } + } + + @Getter + public static class ApplicabilityConfig { + /** + * Map of extra parameters for custom features not available in this client library. The content + * in this map is not serialized under this field's {@code @SerializedName} value. Instead, each + * key/value pair is serialized as if the key is a root-level field (serialized) name in this + * param object. Effectively, this map is flattened to its parent instance. + */ + @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) + Map extraParams; + + /** Required. Specify the scope of this applicability config. */ + @SerializedName("scope") + Scope scope; + + private ApplicabilityConfig(Map extraParams, Scope scope) { + this.extraParams = extraParams; + this.scope = scope; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private Map extraParams; + + private Scope scope; + + /** Finalize and obtain parameter instance from this builder. */ + public CreditGrantCreateParams.ApplicabilityConfig build() { + return new CreditGrantCreateParams.ApplicabilityConfig(this.extraParams, this.scope); + } + + /** + * Add a key/value pair to `extraParams` map. A map is initialized for the first `put/putAll` + * call, and subsequent calls add additional key/value pairs to the original map. See {@link + * CreditGrantCreateParams.ApplicabilityConfig#extraParams} for the field documentation. + */ + public Builder putExtraParam(String key, Object value) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original map. + * See {@link CreditGrantCreateParams.ApplicabilityConfig#extraParams} for the field + * documentation. + */ + public Builder putAllExtraParam(Map map) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.putAll(map); + return this; + } + + /** Required. Specify the scope of this applicability config. */ + public Builder setScope(CreditGrantCreateParams.ApplicabilityConfig.Scope scope) { + this.scope = scope; + return this; + } + } + + @Getter + public static class Scope { + /** + * Map of extra parameters for custom features not available in this client library. The + * content in this map is not serialized under this field's {@code @SerializedName} value. + * Instead, each key/value pair is serialized as if the key is a root-level field (serialized) + * name in this param object. Effectively, this map is flattened to its parent instance. + */ + @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) + Map extraParams; + + /** + * Required. The price type to which credit grants can apply to. We currently + * only support {@code metered} price type. + */ + @SerializedName("price_type") + PriceType priceType; + + private Scope(Map extraParams, PriceType priceType) { + this.extraParams = extraParams; + this.priceType = priceType; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private Map extraParams; + + private PriceType priceType; + + /** Finalize and obtain parameter instance from this builder. */ + public CreditGrantCreateParams.ApplicabilityConfig.Scope build() { + return new CreditGrantCreateParams.ApplicabilityConfig.Scope( + this.extraParams, this.priceType); + } + + /** + * Add a key/value pair to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original + * map. See {@link CreditGrantCreateParams.ApplicabilityConfig.Scope#extraParams} for the + * field documentation. + */ + public Builder putExtraParam(String key, Object value) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original + * map. See {@link CreditGrantCreateParams.ApplicabilityConfig.Scope#extraParams} for the + * field documentation. + */ + public Builder putAllExtraParam(Map map) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.putAll(map); + return this; + } + + /** + * Required. The price type to which credit grants can apply to. We + * currently only support {@code metered} price type. + */ + public Builder setPriceType( + CreditGrantCreateParams.ApplicabilityConfig.Scope.PriceType priceType) { + this.priceType = priceType; + return this; + } + } + + public enum PriceType implements ApiRequestParams.EnumParam { + @SerializedName("metered") + METERED("metered"); + + @Getter(onMethod_ = {@Override}) + private final String value; + + PriceType(String value) { + this.value = value; + } + } + } + } + + public enum Category implements ApiRequestParams.EnumParam { + @SerializedName("paid") + PAID("paid"), + + @SerializedName("promotional") + PROMOTIONAL("promotional"); + + @Getter(onMethod_ = {@Override}) + private final String value; + + Category(String value) { + this.value = value; + } + } +} diff --git a/src/main/java/com/stripe/param/billing/CreditGrantExpireParams.java b/src/main/java/com/stripe/param/billing/CreditGrantExpireParams.java new file mode 100644 index 00000000000..928eb38fe4c --- /dev/null +++ b/src/main/java/com/stripe/param/billing/CreditGrantExpireParams.java @@ -0,0 +1,98 @@ +// File generated from our OpenAPI spec +package com.stripe.param.billing; + +import com.google.gson.annotations.SerializedName; +import com.stripe.net.ApiRequestParams; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.Getter; + +@Getter +public class CreditGrantExpireParams extends ApiRequestParams { + /** Specifies which fields in the response should be expanded. */ + @SerializedName("expand") + List expand; + + /** + * Map of extra parameters for custom features not available in this client library. The content + * in this map is not serialized under this field's {@code @SerializedName} value. Instead, each + * key/value pair is serialized as if the key is a root-level field (serialized) name in this + * param object. Effectively, this map is flattened to its parent instance. + */ + @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) + Map extraParams; + + private CreditGrantExpireParams(List expand, Map extraParams) { + this.expand = expand; + this.extraParams = extraParams; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private List expand; + + private Map extraParams; + + /** Finalize and obtain parameter instance from this builder. */ + public CreditGrantExpireParams build() { + return new CreditGrantExpireParams(this.expand, this.extraParams); + } + + /** + * Add an element to `expand` list. A list is initialized for the first `add/addAll` call, and + * subsequent calls adds additional elements to the original list. See {@link + * CreditGrantExpireParams#expand} for the field documentation. + */ + public Builder addExpand(String element) { + if (this.expand == null) { + this.expand = new ArrayList<>(); + } + this.expand.add(element); + return this; + } + + /** + * Add all elements to `expand` list. A list is initialized for the first `add/addAll` call, and + * subsequent calls adds additional elements to the original list. See {@link + * CreditGrantExpireParams#expand} for the field documentation. + */ + public Builder addAllExpand(List elements) { + if (this.expand == null) { + this.expand = new ArrayList<>(); + } + this.expand.addAll(elements); + return this; + } + + /** + * Add a key/value pair to `extraParams` map. A map is initialized for the first `put/putAll` + * call, and subsequent calls add additional key/value pairs to the original map. See {@link + * CreditGrantExpireParams#extraParams} for the field documentation. + */ + public Builder putExtraParam(String key, Object value) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original map. + * See {@link CreditGrantExpireParams#extraParams} for the field documentation. + */ + public Builder putAllExtraParam(Map map) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.putAll(map); + return this; + } + } +} diff --git a/src/main/java/com/stripe/param/billing/CreditGrantListParams.java b/src/main/java/com/stripe/param/billing/CreditGrantListParams.java new file mode 100644 index 00000000000..0e5ae22ce3f --- /dev/null +++ b/src/main/java/com/stripe/param/billing/CreditGrantListParams.java @@ -0,0 +1,188 @@ +// File generated from our OpenAPI spec +package com.stripe.param.billing; + +import com.google.gson.annotations.SerializedName; +import com.stripe.net.ApiRequestParams; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.Getter; + +@Getter +public class CreditGrantListParams extends ApiRequestParams { + /** Only return credit grants for this customer. */ + @SerializedName("customer") + String customer; + + /** + * A cursor for use in pagination. {@code ending_before} is an object ID that defines your place + * in the list. For instance, if you make a list request and receive 100 objects, starting with + * {@code obj_bar}, your subsequent call can include {@code ending_before=obj_bar} in order to + * fetch the previous page of the list. + */ + @SerializedName("ending_before") + String endingBefore; + + /** Specifies which fields in the response should be expanded. */ + @SerializedName("expand") + List expand; + + /** + * Map of extra parameters for custom features not available in this client library. The content + * in this map is not serialized under this field's {@code @SerializedName} value. Instead, each + * key/value pair is serialized as if the key is a root-level field (serialized) name in this + * param object. Effectively, this map is flattened to its parent instance. + */ + @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) + Map extraParams; + + /** + * A limit on the number of objects to be returned. Limit can range between 1 and 100, and the + * default is 10. + */ + @SerializedName("limit") + Long limit; + + /** + * A cursor for use in pagination. {@code starting_after} is an object ID that defines your place + * in the list. For instance, if you make a list request and receive 100 objects, ending with + * {@code obj_foo}, your subsequent call can include {@code starting_after=obj_foo} in order to + * fetch the next page of the list. + */ + @SerializedName("starting_after") + String startingAfter; + + private CreditGrantListParams( + String customer, + String endingBefore, + List expand, + Map extraParams, + Long limit, + String startingAfter) { + this.customer = customer; + this.endingBefore = endingBefore; + this.expand = expand; + this.extraParams = extraParams; + this.limit = limit; + this.startingAfter = startingAfter; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String customer; + + private String endingBefore; + + private List expand; + + private Map extraParams; + + private Long limit; + + private String startingAfter; + + /** Finalize and obtain parameter instance from this builder. */ + public CreditGrantListParams build() { + return new CreditGrantListParams( + this.customer, + this.endingBefore, + this.expand, + this.extraParams, + this.limit, + this.startingAfter); + } + + /** Only return credit grants for this customer. */ + public Builder setCustomer(String customer) { + this.customer = customer; + return this; + } + + /** + * A cursor for use in pagination. {@code ending_before} is an object ID that defines your place + * in the list. For instance, if you make a list request and receive 100 objects, starting with + * {@code obj_bar}, your subsequent call can include {@code ending_before=obj_bar} in order to + * fetch the previous page of the list. + */ + public Builder setEndingBefore(String endingBefore) { + this.endingBefore = endingBefore; + return this; + } + + /** + * Add an element to `expand` list. A list is initialized for the first `add/addAll` call, and + * subsequent calls adds additional elements to the original list. See {@link + * CreditGrantListParams#expand} for the field documentation. + */ + public Builder addExpand(String element) { + if (this.expand == null) { + this.expand = new ArrayList<>(); + } + this.expand.add(element); + return this; + } + + /** + * Add all elements to `expand` list. A list is initialized for the first `add/addAll` call, and + * subsequent calls adds additional elements to the original list. See {@link + * CreditGrantListParams#expand} for the field documentation. + */ + public Builder addAllExpand(List elements) { + if (this.expand == null) { + this.expand = new ArrayList<>(); + } + this.expand.addAll(elements); + return this; + } + + /** + * Add a key/value pair to `extraParams` map. A map is initialized for the first `put/putAll` + * call, and subsequent calls add additional key/value pairs to the original map. See {@link + * CreditGrantListParams#extraParams} for the field documentation. + */ + public Builder putExtraParam(String key, Object value) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original map. + * See {@link CreditGrantListParams#extraParams} for the field documentation. + */ + public Builder putAllExtraParam(Map map) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.putAll(map); + return this; + } + + /** + * A limit on the number of objects to be returned. Limit can range between 1 and 100, and the + * default is 10. + */ + public Builder setLimit(Long limit) { + this.limit = limit; + return this; + } + + /** + * A cursor for use in pagination. {@code starting_after} is an object ID that defines your + * place in the list. For instance, if you make a list request and receive 100 objects, ending + * with {@code obj_foo}, your subsequent call can include {@code starting_after=obj_foo} in + * order to fetch the next page of the list. + */ + public Builder setStartingAfter(String startingAfter) { + this.startingAfter = startingAfter; + return this; + } + } +} diff --git a/src/main/java/com/stripe/param/billing/CreditGrantRetrieveParams.java b/src/main/java/com/stripe/param/billing/CreditGrantRetrieveParams.java new file mode 100644 index 00000000000..ffec603dd71 --- /dev/null +++ b/src/main/java/com/stripe/param/billing/CreditGrantRetrieveParams.java @@ -0,0 +1,98 @@ +// File generated from our OpenAPI spec +package com.stripe.param.billing; + +import com.google.gson.annotations.SerializedName; +import com.stripe.net.ApiRequestParams; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.Getter; + +@Getter +public class CreditGrantRetrieveParams extends ApiRequestParams { + /** Specifies which fields in the response should be expanded. */ + @SerializedName("expand") + List expand; + + /** + * Map of extra parameters for custom features not available in this client library. The content + * in this map is not serialized under this field's {@code @SerializedName} value. Instead, each + * key/value pair is serialized as if the key is a root-level field (serialized) name in this + * param object. Effectively, this map is flattened to its parent instance. + */ + @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) + Map extraParams; + + private CreditGrantRetrieveParams(List expand, Map extraParams) { + this.expand = expand; + this.extraParams = extraParams; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private List expand; + + private Map extraParams; + + /** Finalize and obtain parameter instance from this builder. */ + public CreditGrantRetrieveParams build() { + return new CreditGrantRetrieveParams(this.expand, this.extraParams); + } + + /** + * Add an element to `expand` list. A list is initialized for the first `add/addAll` call, and + * subsequent calls adds additional elements to the original list. See {@link + * CreditGrantRetrieveParams#expand} for the field documentation. + */ + public Builder addExpand(String element) { + if (this.expand == null) { + this.expand = new ArrayList<>(); + } + this.expand.add(element); + return this; + } + + /** + * Add all elements to `expand` list. A list is initialized for the first `add/addAll` call, and + * subsequent calls adds additional elements to the original list. See {@link + * CreditGrantRetrieveParams#expand} for the field documentation. + */ + public Builder addAllExpand(List elements) { + if (this.expand == null) { + this.expand = new ArrayList<>(); + } + this.expand.addAll(elements); + return this; + } + + /** + * Add a key/value pair to `extraParams` map. A map is initialized for the first `put/putAll` + * call, and subsequent calls add additional key/value pairs to the original map. See {@link + * CreditGrantRetrieveParams#extraParams} for the field documentation. + */ + public Builder putExtraParam(String key, Object value) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original map. + * See {@link CreditGrantRetrieveParams#extraParams} for the field documentation. + */ + public Builder putAllExtraParam(Map map) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.putAll(map); + return this; + } + } +} diff --git a/src/main/java/com/stripe/param/billing/CreditGrantUpdateParams.java b/src/main/java/com/stripe/param/billing/CreditGrantUpdateParams.java new file mode 100644 index 00000000000..e5dca877928 --- /dev/null +++ b/src/main/java/com/stripe/param/billing/CreditGrantUpdateParams.java @@ -0,0 +1,168 @@ +// File generated from our OpenAPI spec +package com.stripe.param.billing; + +import com.google.gson.annotations.SerializedName; +import com.stripe.net.ApiRequestParams; +import com.stripe.param.common.EmptyParam; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.Getter; + +@Getter +public class CreditGrantUpdateParams extends ApiRequestParams { + /** Specifies which fields in the response should be expanded. */ + @SerializedName("expand") + List expand; + + /** + * The time when the credit created by this credit grant will expire. If set to empty, the credit + * will never expire. + */ + @SerializedName("expires_at") + Object expiresAt; + + /** + * Map of extra parameters for custom features not available in this client library. The content + * in this map is not serialized under this field's {@code @SerializedName} value. Instead, each + * key/value pair is serialized as if the key is a root-level field (serialized) name in this + * param object. Effectively, this map is flattened to its parent instance. + */ + @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) + Map extraParams; + + /** + * Set of key-value pairs that you can attach to an object. This can be useful for storing + * additional information about the object (ex: cost basis) in a structured format. + */ + @SerializedName("metadata") + Map metadata; + + private CreditGrantUpdateParams( + List expand, + Object expiresAt, + Map extraParams, + Map metadata) { + this.expand = expand; + this.expiresAt = expiresAt; + this.extraParams = extraParams; + this.metadata = metadata; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private List expand; + + private Object expiresAt; + + private Map extraParams; + + private Map metadata; + + /** Finalize and obtain parameter instance from this builder. */ + public CreditGrantUpdateParams build() { + return new CreditGrantUpdateParams( + this.expand, this.expiresAt, this.extraParams, this.metadata); + } + + /** + * Add an element to `expand` list. A list is initialized for the first `add/addAll` call, and + * subsequent calls adds additional elements to the original list. See {@link + * CreditGrantUpdateParams#expand} for the field documentation. + */ + public Builder addExpand(String element) { + if (this.expand == null) { + this.expand = new ArrayList<>(); + } + this.expand.add(element); + return this; + } + + /** + * Add all elements to `expand` list. A list is initialized for the first `add/addAll` call, and + * subsequent calls adds additional elements to the original list. See {@link + * CreditGrantUpdateParams#expand} for the field documentation. + */ + public Builder addAllExpand(List elements) { + if (this.expand == null) { + this.expand = new ArrayList<>(); + } + this.expand.addAll(elements); + return this; + } + + /** + * The time when the credit created by this credit grant will expire. If set to empty, the + * credit will never expire. + */ + public Builder setExpiresAt(Long expiresAt) { + this.expiresAt = expiresAt; + return this; + } + + /** + * The time when the credit created by this credit grant will expire. If set to empty, the + * credit will never expire. + */ + public Builder setExpiresAt(EmptyParam expiresAt) { + this.expiresAt = expiresAt; + return this; + } + + /** + * Add a key/value pair to `extraParams` map. A map is initialized for the first `put/putAll` + * call, and subsequent calls add additional key/value pairs to the original map. See {@link + * CreditGrantUpdateParams#extraParams} for the field documentation. + */ + public Builder putExtraParam(String key, Object value) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original map. + * See {@link CreditGrantUpdateParams#extraParams} for the field documentation. + */ + public Builder putAllExtraParam(Map map) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.putAll(map); + return this; + } + + /** + * Add a key/value pair to `metadata` map. A map is initialized for the first `put/putAll` call, + * and subsequent calls add additional key/value pairs to the original map. See {@link + * CreditGrantUpdateParams#metadata} for the field documentation. + */ + public Builder putMetadata(String key, String value) { + if (this.metadata == null) { + this.metadata = new HashMap<>(); + } + this.metadata.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `metadata` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original map. + * See {@link CreditGrantUpdateParams#metadata} for the field documentation. + */ + public Builder putAllMetadata(Map map) { + if (this.metadata == null) { + this.metadata = new HashMap<>(); + } + this.metadata.putAll(map); + return this; + } + } +} diff --git a/src/main/java/com/stripe/param/billing/CreditGrantVoidGrantParams.java b/src/main/java/com/stripe/param/billing/CreditGrantVoidGrantParams.java new file mode 100644 index 00000000000..15496e666c1 --- /dev/null +++ b/src/main/java/com/stripe/param/billing/CreditGrantVoidGrantParams.java @@ -0,0 +1,98 @@ +// File generated from our OpenAPI spec +package com.stripe.param.billing; + +import com.google.gson.annotations.SerializedName; +import com.stripe.net.ApiRequestParams; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.Getter; + +@Getter +public class CreditGrantVoidGrantParams extends ApiRequestParams { + /** Specifies which fields in the response should be expanded. */ + @SerializedName("expand") + List expand; + + /** + * Map of extra parameters for custom features not available in this client library. The content + * in this map is not serialized under this field's {@code @SerializedName} value. Instead, each + * key/value pair is serialized as if the key is a root-level field (serialized) name in this + * param object. Effectively, this map is flattened to its parent instance. + */ + @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) + Map extraParams; + + private CreditGrantVoidGrantParams(List expand, Map extraParams) { + this.expand = expand; + this.extraParams = extraParams; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private List expand; + + private Map extraParams; + + /** Finalize and obtain parameter instance from this builder. */ + public CreditGrantVoidGrantParams build() { + return new CreditGrantVoidGrantParams(this.expand, this.extraParams); + } + + /** + * Add an element to `expand` list. A list is initialized for the first `add/addAll` call, and + * subsequent calls adds additional elements to the original list. See {@link + * CreditGrantVoidGrantParams#expand} for the field documentation. + */ + public Builder addExpand(String element) { + if (this.expand == null) { + this.expand = new ArrayList<>(); + } + this.expand.add(element); + return this; + } + + /** + * Add all elements to `expand` list. A list is initialized for the first `add/addAll` call, and + * subsequent calls adds additional elements to the original list. See {@link + * CreditGrantVoidGrantParams#expand} for the field documentation. + */ + public Builder addAllExpand(List elements) { + if (this.expand == null) { + this.expand = new ArrayList<>(); + } + this.expand.addAll(elements); + return this; + } + + /** + * Add a key/value pair to `extraParams` map. A map is initialized for the first `put/putAll` + * call, and subsequent calls add additional key/value pairs to the original map. See {@link + * CreditGrantVoidGrantParams#extraParams} for the field documentation. + */ + public Builder putExtraParam(String key, Object value) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original map. + * See {@link CreditGrantVoidGrantParams#extraParams} for the field documentation. + */ + public Builder putAllExtraParam(Map map) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.putAll(map); + return this; + } + } +} diff --git a/src/main/java/com/stripe/param/billingportal/ConfigurationCreateParams.java b/src/main/java/com/stripe/param/billingportal/ConfigurationCreateParams.java index 758f2a8d823..6408ccf1c9b 100644 --- a/src/main/java/com/stripe/param/billingportal/ConfigurationCreateParams.java +++ b/src/main/java/com/stripe/param/billingportal/ConfigurationCreateParams.java @@ -1137,8 +1137,8 @@ public enum ProrationBehavior implements ApiRequestParams.EnumParam { @Getter public static class SubscriptionUpdate { /** - * Required. The types of subscription updates that are supported. When - * empty, subscriptions are not updateable. + * The types of subscription updates that are supported. When empty, subscriptions are not + * updateable. */ @SerializedName("default_allowed_updates") Object defaultAllowedUpdates; @@ -1156,9 +1156,7 @@ public static class SubscriptionUpdate { @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) Map extraParams; - /** - * Required. The list of up to 10 products that support subscription updates. - */ + /** The list of up to 10 products that support subscription updates. */ @SerializedName("products") Object products; @@ -1251,8 +1249,8 @@ public Builder addAllDefaultAllowedUpdate( } /** - * Required. The types of subscription updates that are supported. When - * empty, subscriptions are not updateable. + * The types of subscription updates that are supported. When empty, subscriptions are not + * updateable. */ public Builder setDefaultAllowedUpdates(EmptyParam defaultAllowedUpdates) { this.defaultAllowedUpdates = defaultAllowedUpdates; @@ -1260,8 +1258,8 @@ public Builder setDefaultAllowedUpdates(EmptyParam defaultAllowedUpdates) { } /** - * Required. The types of subscription updates that are supported. When - * empty, subscriptions are not updateable. + * The types of subscription updates that are supported. When empty, subscriptions are not + * updateable. */ public Builder setDefaultAllowedUpdates( List @@ -1340,19 +1338,13 @@ public Builder addAllProduct( return this; } - /** - * Required. The list of up to 10 products that support subscription - * updates. - */ + /** The list of up to 10 products that support subscription updates. */ public Builder setProducts(EmptyParam products) { this.products = products; return this; } - /** - * Required. The list of up to 10 products that support subscription - * updates. - */ + /** The list of up to 10 products that support subscription updates. */ public Builder setProducts( List products) { this.products = products; diff --git a/src/main/java/com/stripe/param/checkout/SessionCreateParams.java b/src/main/java/com/stripe/param/checkout/SessionCreateParams.java index 06cb2526d0c..e7e75d0b7f0 100644 --- a/src/main/java/com/stripe/param/checkout/SessionCreateParams.java +++ b/src/main/java/com/stripe/param/checkout/SessionCreateParams.java @@ -4001,8 +4001,7 @@ public Builder addAllTaxRate(List elements) { public static class AdjustableQuantity { /** * Required. Set to true if the quantity can be adjusted to any non-negative - * integer. By default customers will be able to remove the line item by setting the quantity - * to 0. + * integer. */ @SerializedName("enabled") Boolean enabled; @@ -4059,8 +4058,7 @@ public SessionCreateParams.LineItem.AdjustableQuantity build() { /** * Required. Set to true if the quantity can be adjusted to any - * non-negative integer. By default customers will be able to remove the line item by - * setting the quantity to 0. + * non-negative integer. */ public Builder setEnabled(Boolean enabled) { this.enabled = enabled; diff --git a/src/main/java/com/stripe/param/terminal/ReaderProcessPaymentIntentParams.java b/src/main/java/com/stripe/param/terminal/ReaderProcessPaymentIntentParams.java index 90aa74e6a9b..52db2d41fa7 100644 --- a/src/main/java/com/stripe/param/terminal/ReaderProcessPaymentIntentParams.java +++ b/src/main/java/com/stripe/param/terminal/ReaderProcessPaymentIntentParams.java @@ -129,6 +129,14 @@ public Builder setProcessConfig(ReaderProcessPaymentIntentParams.ProcessConfig p @Getter public static class ProcessConfig { + /** + * This field indicates whether this payment method can be shown again to its customer in a + * checkout flow. Stripe products such as Checkout and Elements use this field to determine + * whether a payment method can be shown as a saved payment method in a checkout flow. + */ + @SerializedName("allow_redisplay") + AllowRedisplay allowRedisplay; + /** Enables cancel button on transaction screens. */ @SerializedName("enable_customer_cancellation") Boolean enableCustomerCancellation; @@ -151,10 +159,12 @@ public static class ProcessConfig { Tipping tipping; private ProcessConfig( + AllowRedisplay allowRedisplay, Boolean enableCustomerCancellation, Map extraParams, Boolean skipTipping, Tipping tipping) { + this.allowRedisplay = allowRedisplay; this.enableCustomerCancellation = enableCustomerCancellation; this.extraParams = extraParams; this.skipTipping = skipTipping; @@ -166,6 +176,8 @@ public static Builder builder() { } public static class Builder { + private AllowRedisplay allowRedisplay; + private Boolean enableCustomerCancellation; private Map extraParams; @@ -177,7 +189,22 @@ public static class Builder { /** Finalize and obtain parameter instance from this builder. */ public ReaderProcessPaymentIntentParams.ProcessConfig build() { return new ReaderProcessPaymentIntentParams.ProcessConfig( - this.enableCustomerCancellation, this.extraParams, this.skipTipping, this.tipping); + this.allowRedisplay, + this.enableCustomerCancellation, + this.extraParams, + this.skipTipping, + this.tipping); + } + + /** + * This field indicates whether this payment method can be shown again to its customer in a + * checkout flow. Stripe products such as Checkout and Elements use this field to determine + * whether a payment method can be shown as a saved payment method in a checkout flow. + */ + public Builder setAllowRedisplay( + ReaderProcessPaymentIntentParams.ProcessConfig.AllowRedisplay allowRedisplay) { + this.allowRedisplay = allowRedisplay; + return this; } /** Enables cancel button on transaction screens. */ @@ -304,5 +331,23 @@ public Builder putAllExtraParam(Map map) { } } } + + public enum AllowRedisplay implements ApiRequestParams.EnumParam { + @SerializedName("always") + ALWAYS("always"), + + @SerializedName("limited") + LIMITED("limited"), + + @SerializedName("unspecified") + UNSPECIFIED("unspecified"); + + @Getter(onMethod_ = {@Override}) + private final String value; + + AllowRedisplay(String value) { + this.value = value; + } + } } } diff --git a/src/main/java/com/stripe/param/terminal/ReaderProcessSetupIntentParams.java b/src/main/java/com/stripe/param/terminal/ReaderProcessSetupIntentParams.java index 01c63713f69..60715e00b0e 100644 --- a/src/main/java/com/stripe/param/terminal/ReaderProcessSetupIntentParams.java +++ b/src/main/java/com/stripe/param/terminal/ReaderProcessSetupIntentParams.java @@ -11,9 +11,14 @@ @Getter public class ReaderProcessSetupIntentParams extends ApiRequestParams { - /** Customer Consent Collected. */ - @SerializedName("customer_consent_collected") - Boolean customerConsentCollected; + /** + * Required. This field indicates whether this payment method can be shown again + * to its customer in a checkout flow. Stripe products such as Checkout and Elements use this + * field to determine whether a payment method can be shown as a saved payment method in a + * checkout flow. + */ + @SerializedName("allow_redisplay") + AllowRedisplay allowRedisplay; /** Specifies which fields in the response should be expanded. */ @SerializedName("expand") @@ -37,12 +42,12 @@ public class ReaderProcessSetupIntentParams extends ApiRequestParams { String setupIntent; private ReaderProcessSetupIntentParams( - Boolean customerConsentCollected, + AllowRedisplay allowRedisplay, List expand, Map extraParams, ProcessConfig processConfig, String setupIntent) { - this.customerConsentCollected = customerConsentCollected; + this.allowRedisplay = allowRedisplay; this.expand = expand; this.extraParams = extraParams; this.processConfig = processConfig; @@ -54,7 +59,7 @@ public static Builder builder() { } public static class Builder { - private Boolean customerConsentCollected; + private AllowRedisplay allowRedisplay; private List expand; @@ -67,16 +72,17 @@ public static class Builder { /** Finalize and obtain parameter instance from this builder. */ public ReaderProcessSetupIntentParams build() { return new ReaderProcessSetupIntentParams( - this.customerConsentCollected, - this.expand, - this.extraParams, - this.processConfig, - this.setupIntent); + this.allowRedisplay, this.expand, this.extraParams, this.processConfig, this.setupIntent); } - /** Customer Consent Collected. */ - public Builder setCustomerConsentCollected(Boolean customerConsentCollected) { - this.customerConsentCollected = customerConsentCollected; + /** + * Required. This field indicates whether this payment method can be shown + * again to its customer in a checkout flow. Stripe products such as Checkout and Elements use + * this field to determine whether a payment method can be shown as a saved payment method in a + * checkout flow. + */ + public Builder setAllowRedisplay(ReaderProcessSetupIntentParams.AllowRedisplay allowRedisplay) { + this.allowRedisplay = allowRedisplay; return this; } @@ -214,4 +220,22 @@ public Builder putAllExtraParam(Map map) { } } } + + public enum AllowRedisplay implements ApiRequestParams.EnumParam { + @SerializedName("always") + ALWAYS("always"), + + @SerializedName("limited") + LIMITED("limited"), + + @SerializedName("unspecified") + UNSPECIFIED("unspecified"); + + @Getter(onMethod_ = {@Override}) + private final String value; + + AllowRedisplay(String value) { + this.value = value; + } + } } diff --git a/src/main/java/com/stripe/param/v2/billing/MeterEventAdjustmentCreateParams.java b/src/main/java/com/stripe/param/v2/billing/MeterEventAdjustmentCreateParams.java new file mode 100644 index 00000000000..18f0706e9bb --- /dev/null +++ b/src/main/java/com/stripe/param/v2/billing/MeterEventAdjustmentCreateParams.java @@ -0,0 +1,203 @@ +// File generated from our OpenAPI spec +package com.stripe.param.v2.billing; + +import com.google.gson.annotations.SerializedName; +import com.stripe.net.ApiRequestParams; +import java.util.HashMap; +import java.util.Map; +import lombok.Getter; + +@Getter +public class MeterEventAdjustmentCreateParams extends ApiRequestParams { + /** Required. Specifies which event to cancel. */ + @SerializedName("cancel") + Cancel cancel; + + /** + * Required. The name of the meter event. Corresponds with the {@code event_name} + * field on a meter. + */ + @SerializedName("event_name") + String eventName; + + /** + * Map of extra parameters for custom features not available in this client library. The content + * in this map is not serialized under this field's {@code @SerializedName} value. Instead, each + * key/value pair is serialized as if the key is a root-level field (serialized) name in this + * param object. Effectively, this map is flattened to its parent instance. + */ + @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) + Map extraParams; + + /** + * Required. Specifies whether to cancel a single event or a range of events for + * a time period. Time period cancellation is not supported yet. + */ + @SerializedName("type") + Type type; + + private MeterEventAdjustmentCreateParams( + Cancel cancel, String eventName, Map extraParams, Type type) { + this.cancel = cancel; + this.eventName = eventName; + this.extraParams = extraParams; + this.type = type; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private Cancel cancel; + + private String eventName; + + private Map extraParams; + + private Type type; + + /** Finalize and obtain parameter instance from this builder. */ + public MeterEventAdjustmentCreateParams build() { + return new MeterEventAdjustmentCreateParams( + this.cancel, this.eventName, this.extraParams, this.type); + } + + /** Required. Specifies which event to cancel. */ + public Builder setCancel(MeterEventAdjustmentCreateParams.Cancel cancel) { + this.cancel = cancel; + return this; + } + + /** + * Required. The name of the meter event. Corresponds with the {@code + * event_name} field on a meter. + */ + public Builder setEventName(String eventName) { + this.eventName = eventName; + return this; + } + + /** + * Add a key/value pair to `extraParams` map. A map is initialized for the first `put/putAll` + * call, and subsequent calls add additional key/value pairs to the original map. See {@link + * MeterEventAdjustmentCreateParams#extraParams} for the field documentation. + */ + public Builder putExtraParam(String key, Object value) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original map. + * See {@link MeterEventAdjustmentCreateParams#extraParams} for the field documentation. + */ + public Builder putAllExtraParam(Map map) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.putAll(map); + return this; + } + + /** + * Required. Specifies whether to cancel a single event or a range of events + * for a time period. Time period cancellation is not supported yet. + */ + public Builder setType(MeterEventAdjustmentCreateParams.Type type) { + this.type = type; + return this; + } + } + + @Getter + public static class Cancel { + /** + * Map of extra parameters for custom features not available in this client library. The content + * in this map is not serialized under this field's {@code @SerializedName} value. Instead, each + * key/value pair is serialized as if the key is a root-level field (serialized) name in this + * param object. Effectively, this map is flattened to its parent instance. + */ + @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) + Map extraParams; + + /** + * Required. Unique identifier for the event. You can only cancel events within + * 24 hours of Stripe receiving them. + */ + @SerializedName("identifier") + String identifier; + + private Cancel(Map extraParams, String identifier) { + this.extraParams = extraParams; + this.identifier = identifier; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private Map extraParams; + + private String identifier; + + /** Finalize and obtain parameter instance from this builder. */ + public MeterEventAdjustmentCreateParams.Cancel build() { + return new MeterEventAdjustmentCreateParams.Cancel(this.extraParams, this.identifier); + } + + /** + * Add a key/value pair to `extraParams` map. A map is initialized for the first `put/putAll` + * call, and subsequent calls add additional key/value pairs to the original map. See {@link + * MeterEventAdjustmentCreateParams.Cancel#extraParams} for the field documentation. + */ + public Builder putExtraParam(String key, Object value) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original map. + * See {@link MeterEventAdjustmentCreateParams.Cancel#extraParams} for the field + * documentation. + */ + public Builder putAllExtraParam(Map map) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.putAll(map); + return this; + } + + /** + * Required. Unique identifier for the event. You can only cancel events + * within 24 hours of Stripe receiving them. + */ + public Builder setIdentifier(String identifier) { + this.identifier = identifier; + return this; + } + } + } + + public enum Type implements ApiRequestParams.EnumParam { + @SerializedName("cancel") + CANCEL("cancel"); + + @Getter(onMethod_ = {@Override}) + private final String value; + + Type(String value) { + this.value = value; + } + } +} diff --git a/src/main/java/com/stripe/param/v2/billing/MeterEventCreateParams.java b/src/main/java/com/stripe/param/v2/billing/MeterEventCreateParams.java new file mode 100644 index 00000000000..2b3655f5ac6 --- /dev/null +++ b/src/main/java/com/stripe/param/v2/billing/MeterEventCreateParams.java @@ -0,0 +1,166 @@ +// File generated from our OpenAPI spec +package com.stripe.param.v2.billing; + +import com.google.gson.annotations.SerializedName; +import com.stripe.net.ApiRequestParams; +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; +import lombok.Getter; + +@Getter +public class MeterEventCreateParams extends ApiRequestParams { + /** + * Required. The name of the meter event. Corresponds with the {@code event_name} + * field on a meter. + */ + @SerializedName("event_name") + String eventName; + + /** + * Map of extra parameters for custom features not available in this client library. The content + * in this map is not serialized under this field's {@code @SerializedName} value. Instead, each + * key/value pair is serialized as if the key is a root-level field (serialized) name in this + * param object. Effectively, this map is flattened to its parent instance. + */ + @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) + Map extraParams; + + /** + * A unique identifier for the event. If not provided, one will be generated. We recommend using a + * globally unique identifier for this. We’ll enforce uniqueness within a rolling 24 hour period. + */ + @SerializedName("identifier") + String identifier; + + /** + * Required. The payload of the event. This must contain the fields corresponding + * to a meter’s {@code customer_mapping.event_payload_key} (default is {@code stripe_customer_id}) + * and {@code value_settings.event_payload_key} (default is {@code value}). Read more about the payload. + */ + @SerializedName("payload") + Map payload; + + /** + * The time of the event. Must be within the past 35 calendar days or up to 5 minutes in the + * future. Defaults to current timestamp if not specified. + */ + @SerializedName("timestamp") + Instant timestamp; + + private MeterEventCreateParams( + String eventName, + Map extraParams, + String identifier, + Map payload, + Instant timestamp) { + this.eventName = eventName; + this.extraParams = extraParams; + this.identifier = identifier; + this.payload = payload; + this.timestamp = timestamp; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String eventName; + + private Map extraParams; + + private String identifier; + + private Map payload; + + private Instant timestamp; + + /** Finalize and obtain parameter instance from this builder. */ + public MeterEventCreateParams build() { + return new MeterEventCreateParams( + this.eventName, this.extraParams, this.identifier, this.payload, this.timestamp); + } + + /** + * Required. The name of the meter event. Corresponds with the {@code + * event_name} field on a meter. + */ + public Builder setEventName(String eventName) { + this.eventName = eventName; + return this; + } + + /** + * Add a key/value pair to `extraParams` map. A map is initialized for the first `put/putAll` + * call, and subsequent calls add additional key/value pairs to the original map. See {@link + * MeterEventCreateParams#extraParams} for the field documentation. + */ + public Builder putExtraParam(String key, Object value) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original map. + * See {@link MeterEventCreateParams#extraParams} for the field documentation. + */ + public Builder putAllExtraParam(Map map) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.putAll(map); + return this; + } + + /** + * A unique identifier for the event. If not provided, one will be generated. We recommend using + * a globally unique identifier for this. We’ll enforce uniqueness within a rolling 24 hour + * period. + */ + public Builder setIdentifier(String identifier) { + this.identifier = identifier; + return this; + } + + /** + * Add a key/value pair to `payload` map. A map is initialized for the first `put/putAll` call, + * and subsequent calls add additional key/value pairs to the original map. See {@link + * MeterEventCreateParams#payload} for the field documentation. + */ + public Builder putPayload(String key, String value) { + if (this.payload == null) { + this.payload = new HashMap<>(); + } + this.payload.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `payload` map. A map is initialized for the first `put/putAll` + * call, and subsequent calls add additional key/value pairs to the original map. See {@link + * MeterEventCreateParams#payload} for the field documentation. + */ + public Builder putAllPayload(Map map) { + if (this.payload == null) { + this.payload = new HashMap<>(); + } + this.payload.putAll(map); + return this; + } + + /** + * The time of the event. Must be within the past 35 calendar days or up to 5 minutes in the + * future. Defaults to current timestamp if not specified. + */ + public Builder setTimestamp(Instant timestamp) { + this.timestamp = timestamp; + return this; + } + } +} diff --git a/src/main/java/com/stripe/param/v2/billing/MeterEventStreamCreateParams.java b/src/main/java/com/stripe/param/v2/billing/MeterEventStreamCreateParams.java new file mode 100644 index 00000000000..a47898d5037 --- /dev/null +++ b/src/main/java/com/stripe/param/v2/billing/MeterEventStreamCreateParams.java @@ -0,0 +1,259 @@ +// File generated from our OpenAPI spec +package com.stripe.param.v2.billing; + +import com.google.gson.annotations.SerializedName; +import com.stripe.net.ApiRequestParams; +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.Getter; + +@Getter +public class MeterEventStreamCreateParams extends ApiRequestParams { + /** Required. List of meter events to include in the request. */ + @SerializedName("events") + List events; + + /** + * Map of extra parameters for custom features not available in this client library. The content + * in this map is not serialized under this field's {@code @SerializedName} value. Instead, each + * key/value pair is serialized as if the key is a root-level field (serialized) name in this + * param object. Effectively, this map is flattened to its parent instance. + */ + @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) + Map extraParams; + + private MeterEventStreamCreateParams( + List events, Map extraParams) { + this.events = events; + this.extraParams = extraParams; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private List events; + + private Map extraParams; + + /** Finalize and obtain parameter instance from this builder. */ + public MeterEventStreamCreateParams build() { + return new MeterEventStreamCreateParams(this.events, this.extraParams); + } + + /** + * Add an element to `events` list. A list is initialized for the first `add/addAll` call, and + * subsequent calls adds additional elements to the original list. See {@link + * MeterEventStreamCreateParams#events} for the field documentation. + */ + public Builder addEvent(MeterEventStreamCreateParams.Event element) { + if (this.events == null) { + this.events = new ArrayList<>(); + } + this.events.add(element); + return this; + } + + /** + * Add all elements to `events` list. A list is initialized for the first `add/addAll` call, and + * subsequent calls adds additional elements to the original list. See {@link + * MeterEventStreamCreateParams#events} for the field documentation. + */ + public Builder addAllEvent(List elements) { + if (this.events == null) { + this.events = new ArrayList<>(); + } + this.events.addAll(elements); + return this; + } + + /** + * Add a key/value pair to `extraParams` map. A map is initialized for the first `put/putAll` + * call, and subsequent calls add additional key/value pairs to the original map. See {@link + * MeterEventStreamCreateParams#extraParams} for the field documentation. + */ + public Builder putExtraParam(String key, Object value) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original map. + * See {@link MeterEventStreamCreateParams#extraParams} for the field documentation. + */ + public Builder putAllExtraParam(Map map) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.putAll(map); + return this; + } + } + + @Getter + public static class Event { + /** + * Required. The name of the meter event. Corresponds with the {@code + * event_name} field on a meter. + */ + @SerializedName("event_name") + String eventName; + + /** + * Map of extra parameters for custom features not available in this client library. The content + * in this map is not serialized under this field's {@code @SerializedName} value. Instead, each + * key/value pair is serialized as if the key is a root-level field (serialized) name in this + * param object. Effectively, this map is flattened to its parent instance. + */ + @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) + Map extraParams; + + /** + * A unique identifier for the event. If not provided, one will be generated. We recommend using + * a globally unique identifier for this. We’ll enforce uniqueness within a rolling 24 hour + * period. + */ + @SerializedName("identifier") + String identifier; + + /** + * Required. The payload of the event. This must contain the fields + * corresponding to a meter’s {@code customer_mapping.event_payload_key} (default is {@code + * stripe_customer_id}) and {@code value_settings.event_payload_key} (default is {@code value}). + * Read more about the payload. + */ + @SerializedName("payload") + Map payload; + + /** + * The time of the event. Must be within the past 35 calendar days or up to 5 minutes in the + * future. Defaults to current timestamp if not specified. + */ + @SerializedName("timestamp") + Instant timestamp; + + private Event( + String eventName, + Map extraParams, + String identifier, + Map payload, + Instant timestamp) { + this.eventName = eventName; + this.extraParams = extraParams; + this.identifier = identifier; + this.payload = payload; + this.timestamp = timestamp; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String eventName; + + private Map extraParams; + + private String identifier; + + private Map payload; + + private Instant timestamp; + + /** Finalize and obtain parameter instance from this builder. */ + public MeterEventStreamCreateParams.Event build() { + return new MeterEventStreamCreateParams.Event( + this.eventName, this.extraParams, this.identifier, this.payload, this.timestamp); + } + + /** + * Required. The name of the meter event. Corresponds with the {@code + * event_name} field on a meter. + */ + public Builder setEventName(String eventName) { + this.eventName = eventName; + return this; + } + + /** + * Add a key/value pair to `extraParams` map. A map is initialized for the first `put/putAll` + * call, and subsequent calls add additional key/value pairs to the original map. See {@link + * MeterEventStreamCreateParams.Event#extraParams} for the field documentation. + */ + public Builder putExtraParam(String key, Object value) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original map. + * See {@link MeterEventStreamCreateParams.Event#extraParams} for the field documentation. + */ + public Builder putAllExtraParam(Map map) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.putAll(map); + return this; + } + + /** + * A unique identifier for the event. If not provided, one will be generated. We recommend + * using a globally unique identifier for this. We’ll enforce uniqueness within a rolling 24 + * hour period. + */ + public Builder setIdentifier(String identifier) { + this.identifier = identifier; + return this; + } + + /** + * Add a key/value pair to `payload` map. A map is initialized for the first `put/putAll` + * call, and subsequent calls add additional key/value pairs to the original map. See {@link + * MeterEventStreamCreateParams.Event#payload} for the field documentation. + */ + public Builder putPayload(String key, String value) { + if (this.payload == null) { + this.payload = new HashMap<>(); + } + this.payload.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `payload` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original map. + * See {@link MeterEventStreamCreateParams.Event#payload} for the field documentation. + */ + public Builder putAllPayload(Map map) { + if (this.payload == null) { + this.payload = new HashMap<>(); + } + this.payload.putAll(map); + return this; + } + + /** + * The time of the event. Must be within the past 35 calendar days or up to 5 minutes in the + * future. Defaults to current timestamp if not specified. + */ + public Builder setTimestamp(Instant timestamp) { + this.timestamp = timestamp; + return this; + } + } + } +} diff --git a/src/main/java/com/stripe/param/v2/core/EventListParams.java b/src/main/java/com/stripe/param/v2/core/EventListParams.java new file mode 100644 index 00000000000..46aa46a383b --- /dev/null +++ b/src/main/java/com/stripe/param/v2/core/EventListParams.java @@ -0,0 +1,103 @@ +// File generated from our OpenAPI spec +package com.stripe.param.v2.core; + +import com.google.gson.annotations.SerializedName; +import com.stripe.net.ApiRequestParams; +import java.util.HashMap; +import java.util.Map; +import lombok.Getter; + +@Getter +public class EventListParams extends ApiRequestParams { + /** + * Map of extra parameters for custom features not available in this client library. The content + * in this map is not serialized under this field's {@code @SerializedName} value. Instead, each + * key/value pair is serialized as if the key is a root-level field (serialized) name in this + * param object. Effectively, this map is flattened to its parent instance. + */ + @SerializedName(ApiRequestParams.EXTRA_PARAMS_KEY) + Map extraParams; + + /** The page size. */ + @SerializedName("limit") + Integer limit; + + /** Required. Primary object ID used to retrieve related events. */ + @SerializedName("object_id") + String objectId; + + /** The requested page number. */ + @SerializedName("page") + String page; + + private EventListParams( + Map extraParams, Integer limit, String objectId, String page) { + this.extraParams = extraParams; + this.limit = limit; + this.objectId = objectId; + this.page = page; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private Map extraParams; + + private Integer limit; + + private String objectId; + + private String page; + + /** Finalize and obtain parameter instance from this builder. */ + public EventListParams build() { + return new EventListParams(this.extraParams, this.limit, this.objectId, this.page); + } + + /** + * Add a key/value pair to `extraParams` map. A map is initialized for the first `put/putAll` + * call, and subsequent calls add additional key/value pairs to the original map. See {@link + * EventListParams#extraParams} for the field documentation. + */ + public Builder putExtraParam(String key, Object value) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.put(key, value); + return this; + } + + /** + * Add all map key/value pairs to `extraParams` map. A map is initialized for the first + * `put/putAll` call, and subsequent calls add additional key/value pairs to the original map. + * See {@link EventListParams#extraParams} for the field documentation. + */ + public Builder putAllExtraParam(Map map) { + if (this.extraParams == null) { + this.extraParams = new HashMap<>(); + } + this.extraParams.putAll(map); + return this; + } + + /** The page size. */ + public Builder setLimit(Integer limit) { + this.limit = limit; + return this; + } + + /** Required. Primary object ID used to retrieve related events. */ + public Builder setObjectId(String objectId) { + this.objectId = objectId; + return this; + } + + /** The requested page number. */ + public Builder setPage(String page) { + this.page = page; + return this; + } + } +} diff --git a/src/main/java/com/stripe/service/BillingService.java b/src/main/java/com/stripe/service/BillingService.java index f8281269a70..e92df43aaac 100644 --- a/src/main/java/com/stripe/service/BillingService.java +++ b/src/main/java/com/stripe/service/BillingService.java @@ -13,6 +13,18 @@ public com.stripe.service.billing.AlertService alerts() { return new com.stripe.service.billing.AlertService(this.getResponseGetter()); } + public com.stripe.service.billing.CreditBalanceSummaryService creditBalanceSummary() { + return new com.stripe.service.billing.CreditBalanceSummaryService(this.getResponseGetter()); + } + + public com.stripe.service.billing.CreditBalanceTransactionService creditBalanceTransactions() { + return new com.stripe.service.billing.CreditBalanceTransactionService(this.getResponseGetter()); + } + + public com.stripe.service.billing.CreditGrantService creditGrants() { + return new com.stripe.service.billing.CreditGrantService(this.getResponseGetter()); + } + public com.stripe.service.billing.MeterEventAdjustmentService meterEventAdjustments() { return new com.stripe.service.billing.MeterEventAdjustmentService(this.getResponseGetter()); } diff --git a/src/main/java/com/stripe/service/SubscriptionService.java b/src/main/java/com/stripe/service/SubscriptionService.java index 355c74be7cf..e716e23a4b7 100644 --- a/src/main/java/com/stripe/service/SubscriptionService.java +++ b/src/main/java/com/stripe/service/SubscriptionService.java @@ -28,17 +28,17 @@ public SubscriptionService(StripeResponseGetter responseGetter) { } /** - * Cancels a customer’s subscription immediately. The customer will not be charged again for the - * subscription. + * Cancels a customer’s subscription immediately. The customer won’t be charged again for the + * subscription. After it’s canceled, you can no longer update the subscription or its metadata. * - *

Note, however, that any pending invoice items that you’ve created will still be charged for - * at the end of the period, unless manually deleted. If you’ve set the - * subscription to cancel at the end of the period, any pending prorations will also be left in - * place and collected at the end of the period. But if the subscription is set to cancel - * immediately, pending prorations will be removed. + *

Any pending invoice items that you’ve created are still charged at the end of the period, + * unless manually deleted. If you’ve + * set the subscription to cancel at the end of the period, any pending prorations are also left + * in place and collected at the end of the period. But if the subscription is set to cancel + * immediately, pending prorations are removed. * - *

By default, upon subscription cancellation, Stripe will stop automatic collection of all + *

By default, upon subscription cancellation, Stripe stops automatic collection of all * finalized invoices for the customer. This is intended to prevent unexpected payment attempts * after the customer has canceled a subscription. However, you can resume automatic collection of * the invoices manually after subscription cancellation to have us proceed. Or, you could check @@ -49,17 +49,17 @@ public Subscription cancel(String subscriptionExposedId, SubscriptionCancelParam return cancel(subscriptionExposedId, params, (RequestOptions) null); } /** - * Cancels a customer’s subscription immediately. The customer will not be charged again for the - * subscription. + * Cancels a customer’s subscription immediately. The customer won’t be charged again for the + * subscription. After it’s canceled, you can no longer update the subscription or its metadata. * - *

Note, however, that any pending invoice items that you’ve created will still be charged for - * at the end of the period, unless manually deleted. If you’ve set the - * subscription to cancel at the end of the period, any pending prorations will also be left in - * place and collected at the end of the period. But if the subscription is set to cancel - * immediately, pending prorations will be removed. + *

Any pending invoice items that you’ve created are still charged at the end of the period, + * unless manually deleted. If you’ve + * set the subscription to cancel at the end of the period, any pending prorations are also left + * in place and collected at the end of the period. But if the subscription is set to cancel + * immediately, pending prorations are removed. * - *

By default, upon subscription cancellation, Stripe will stop automatic collection of all + *

By default, upon subscription cancellation, Stripe stops automatic collection of all * finalized invoices for the customer. This is intended to prevent unexpected payment attempts * after the customer has canceled a subscription. However, you can resume automatic collection of * the invoices manually after subscription cancellation to have us proceed. Or, you could check @@ -70,17 +70,17 @@ public Subscription cancel(String subscriptionExposedId, RequestOptions options) return cancel(subscriptionExposedId, (SubscriptionCancelParams) null, options); } /** - * Cancels a customer’s subscription immediately. The customer will not be charged again for the - * subscription. + * Cancels a customer’s subscription immediately. The customer won’t be charged again for the + * subscription. After it’s canceled, you can no longer update the subscription or its metadata. * - *

Note, however, that any pending invoice items that you’ve created will still be charged for - * at the end of the period, unless manually deleted. If you’ve set the - * subscription to cancel at the end of the period, any pending prorations will also be left in - * place and collected at the end of the period. But if the subscription is set to cancel - * immediately, pending prorations will be removed. + *

Any pending invoice items that you’ve created are still charged at the end of the period, + * unless manually deleted. If you’ve + * set the subscription to cancel at the end of the period, any pending prorations are also left + * in place and collected at the end of the period. But if the subscription is set to cancel + * immediately, pending prorations are removed. * - *

By default, upon subscription cancellation, Stripe will stop automatic collection of all + *

By default, upon subscription cancellation, Stripe stops automatic collection of all * finalized invoices for the customer. This is intended to prevent unexpected payment attempts * after the customer has canceled a subscription. However, you can resume automatic collection of * the invoices manually after subscription cancellation to have us proceed. Or, you could check @@ -90,17 +90,17 @@ public Subscription cancel(String subscriptionExposedId) throws StripeException return cancel(subscriptionExposedId, (SubscriptionCancelParams) null, (RequestOptions) null); } /** - * Cancels a customer’s subscription immediately. The customer will not be charged again for the - * subscription. + * Cancels a customer’s subscription immediately. The customer won’t be charged again for the + * subscription. After it’s canceled, you can no longer update the subscription or its metadata. * - *

Note, however, that any pending invoice items that you’ve created will still be charged for - * at the end of the period, unless manually deleted. If you’ve set the - * subscription to cancel at the end of the period, any pending prorations will also be left in - * place and collected at the end of the period. But if the subscription is set to cancel - * immediately, pending prorations will be removed. + *

Any pending invoice items that you’ve created are still charged at the end of the period, + * unless manually deleted. If you’ve + * set the subscription to cancel at the end of the period, any pending prorations are also left + * in place and collected at the end of the period. But if the subscription is set to cancel + * immediately, pending prorations are removed. * - *

By default, upon subscription cancellation, Stripe will stop automatic collection of all + *

By default, upon subscription cancellation, Stripe stops automatic collection of all * finalized invoices for the customer. This is intended to prevent unexpected payment attempts * after the customer has canceled a subscription. However, you can resume automatic collection of * the invoices manually after subscription cancellation to have us proceed. Or, you could check diff --git a/src/main/java/com/stripe/service/V2Services.java b/src/main/java/com/stripe/service/V2Services.java new file mode 100644 index 00000000000..6138c347dd4 --- /dev/null +++ b/src/main/java/com/stripe/service/V2Services.java @@ -0,0 +1,19 @@ +// File generated from our OpenAPI spec +package com.stripe.service; + +import com.stripe.net.ApiService; +import com.stripe.net.StripeResponseGetter; + +public final class V2Services extends ApiService { + public V2Services(StripeResponseGetter responseGetter) { + super(responseGetter); + } + + public com.stripe.service.v2.BillingService billing() { + return new com.stripe.service.v2.BillingService(this.getResponseGetter()); + } + + public com.stripe.service.v2.CoreService core() { + return new com.stripe.service.v2.CoreService(this.getResponseGetter()); + } +} diff --git a/src/main/java/com/stripe/service/billing/CreditBalanceSummaryService.java b/src/main/java/com/stripe/service/billing/CreditBalanceSummaryService.java new file mode 100644 index 00000000000..b20e3ab4cf9 --- /dev/null +++ b/src/main/java/com/stripe/service/billing/CreditBalanceSummaryService.java @@ -0,0 +1,38 @@ +// File generated from our OpenAPI spec +package com.stripe.service.billing; + +import com.stripe.exception.StripeException; +import com.stripe.model.billing.CreditBalanceSummary; +import com.stripe.net.ApiRequest; +import com.stripe.net.ApiRequestParams; +import com.stripe.net.ApiResource; +import com.stripe.net.ApiService; +import com.stripe.net.BaseAddress; +import com.stripe.net.RequestOptions; +import com.stripe.net.StripeResponseGetter; +import com.stripe.param.billing.CreditBalanceSummaryRetrieveParams; + +public final class CreditBalanceSummaryService extends ApiService { + public CreditBalanceSummaryService(StripeResponseGetter responseGetter) { + super(responseGetter); + } + + /** Retrieves the credit balance summary for a customer. */ + public CreditBalanceSummary retrieve(CreditBalanceSummaryRetrieveParams params) + throws StripeException { + return retrieve(params, (RequestOptions) null); + } + /** Retrieves the credit balance summary for a customer. */ + public CreditBalanceSummary retrieve( + CreditBalanceSummaryRetrieveParams params, RequestOptions options) throws StripeException { + String path = "/v1/billing/credit_balance_summary"; + ApiRequest request = + new ApiRequest( + BaseAddress.API, + ApiResource.RequestMethod.GET, + path, + ApiRequestParams.paramsToMap(params), + options); + return this.request(request, CreditBalanceSummary.class); + } +} diff --git a/src/main/java/com/stripe/service/billing/CreditBalanceTransactionService.java b/src/main/java/com/stripe/service/billing/CreditBalanceTransactionService.java new file mode 100644 index 00000000000..101ab5fa719 --- /dev/null +++ b/src/main/java/com/stripe/service/billing/CreditBalanceTransactionService.java @@ -0,0 +1,71 @@ +// File generated from our OpenAPI spec +package com.stripe.service.billing; + +import com.google.gson.reflect.TypeToken; +import com.stripe.exception.StripeException; +import com.stripe.model.StripeCollection; +import com.stripe.model.billing.CreditBalanceTransaction; +import com.stripe.net.ApiRequest; +import com.stripe.net.ApiRequestParams; +import com.stripe.net.ApiResource; +import com.stripe.net.ApiService; +import com.stripe.net.BaseAddress; +import com.stripe.net.RequestOptions; +import com.stripe.net.StripeResponseGetter; +import com.stripe.param.billing.CreditBalanceTransactionListParams; +import com.stripe.param.billing.CreditBalanceTransactionRetrieveParams; + +public final class CreditBalanceTransactionService extends ApiService { + public CreditBalanceTransactionService(StripeResponseGetter responseGetter) { + super(responseGetter); + } + + /** Retrieve a list of credit balance transactions. */ + public StripeCollection list(CreditBalanceTransactionListParams params) + throws StripeException { + return list(params, (RequestOptions) null); + } + /** Retrieve a list of credit balance transactions. */ + public StripeCollection list( + CreditBalanceTransactionListParams params, RequestOptions options) throws StripeException { + String path = "/v1/billing/credit_balance_transactions"; + ApiRequest request = + new ApiRequest( + BaseAddress.API, + ApiResource.RequestMethod.GET, + path, + ApiRequestParams.paramsToMap(params), + options); + return this.request( + request, new TypeToken>() {}.getType()); + } + /** Retrieves a credit balance transaction. */ + public CreditBalanceTransaction retrieve(String id, CreditBalanceTransactionRetrieveParams params) + throws StripeException { + return retrieve(id, params, (RequestOptions) null); + } + /** Retrieves a credit balance transaction. */ + public CreditBalanceTransaction retrieve(String id, RequestOptions options) + throws StripeException { + return retrieve(id, (CreditBalanceTransactionRetrieveParams) null, options); + } + /** Retrieves a credit balance transaction. */ + public CreditBalanceTransaction retrieve(String id) throws StripeException { + return retrieve(id, (CreditBalanceTransactionRetrieveParams) null, (RequestOptions) null); + } + /** Retrieves a credit balance transaction. */ + public CreditBalanceTransaction retrieve( + String id, CreditBalanceTransactionRetrieveParams params, RequestOptions options) + throws StripeException { + String path = + String.format("/v1/billing/credit_balance_transactions/%s", ApiResource.urlEncodeId(id)); + ApiRequest request = + new ApiRequest( + BaseAddress.API, + ApiResource.RequestMethod.GET, + path, + ApiRequestParams.paramsToMap(params), + options); + return this.request(request, CreditBalanceTransaction.class); + } +} diff --git a/src/main/java/com/stripe/service/billing/CreditGrantService.java b/src/main/java/com/stripe/service/billing/CreditGrantService.java new file mode 100644 index 00000000000..adb40a05ab6 --- /dev/null +++ b/src/main/java/com/stripe/service/billing/CreditGrantService.java @@ -0,0 +1,170 @@ +// File generated from our OpenAPI spec +package com.stripe.service.billing; + +import com.google.gson.reflect.TypeToken; +import com.stripe.exception.StripeException; +import com.stripe.model.StripeCollection; +import com.stripe.model.billing.CreditGrant; +import com.stripe.net.ApiRequest; +import com.stripe.net.ApiRequestParams; +import com.stripe.net.ApiResource; +import com.stripe.net.ApiService; +import com.stripe.net.BaseAddress; +import com.stripe.net.RequestOptions; +import com.stripe.net.StripeResponseGetter; +import com.stripe.param.billing.CreditGrantCreateParams; +import com.stripe.param.billing.CreditGrantExpireParams; +import com.stripe.param.billing.CreditGrantListParams; +import com.stripe.param.billing.CreditGrantRetrieveParams; +import com.stripe.param.billing.CreditGrantUpdateParams; +import com.stripe.param.billing.CreditGrantVoidGrantParams; + +public final class CreditGrantService extends ApiService { + public CreditGrantService(StripeResponseGetter responseGetter) { + super(responseGetter); + } + + /** Retrieve a list of credit grants. */ + public StripeCollection list(CreditGrantListParams params) throws StripeException { + return list(params, (RequestOptions) null); + } + /** Retrieve a list of credit grants. */ + public StripeCollection list(RequestOptions options) throws StripeException { + return list((CreditGrantListParams) null, options); + } + /** Retrieve a list of credit grants. */ + public StripeCollection list() throws StripeException { + return list((CreditGrantListParams) null, (RequestOptions) null); + } + /** Retrieve a list of credit grants. */ + public StripeCollection list(CreditGrantListParams params, RequestOptions options) + throws StripeException { + String path = "/v1/billing/credit_grants"; + ApiRequest request = + new ApiRequest( + BaseAddress.API, + ApiResource.RequestMethod.GET, + path, + ApiRequestParams.paramsToMap(params), + options); + return this.request(request, new TypeToken>() {}.getType()); + } + /** Creates a credit grant. */ + public CreditGrant create(CreditGrantCreateParams params) throws StripeException { + return create(params, (RequestOptions) null); + } + /** Creates a credit grant. */ + public CreditGrant create(CreditGrantCreateParams params, RequestOptions options) + throws StripeException { + String path = "/v1/billing/credit_grants"; + ApiRequest request = + new ApiRequest( + BaseAddress.API, + ApiResource.RequestMethod.POST, + path, + ApiRequestParams.paramsToMap(params), + options); + return this.request(request, CreditGrant.class); + } + /** Retrieves a credit grant. */ + public CreditGrant retrieve(String id, CreditGrantRetrieveParams params) throws StripeException { + return retrieve(id, params, (RequestOptions) null); + } + /** Retrieves a credit grant. */ + public CreditGrant retrieve(String id, RequestOptions options) throws StripeException { + return retrieve(id, (CreditGrantRetrieveParams) null, options); + } + /** Retrieves a credit grant. */ + public CreditGrant retrieve(String id) throws StripeException { + return retrieve(id, (CreditGrantRetrieveParams) null, (RequestOptions) null); + } + /** Retrieves a credit grant. */ + public CreditGrant retrieve(String id, CreditGrantRetrieveParams params, RequestOptions options) + throws StripeException { + String path = String.format("/v1/billing/credit_grants/%s", ApiResource.urlEncodeId(id)); + ApiRequest request = + new ApiRequest( + BaseAddress.API, + ApiResource.RequestMethod.GET, + path, + ApiRequestParams.paramsToMap(params), + options); + return this.request(request, CreditGrant.class); + } + /** Updates a credit grant. */ + public CreditGrant update(String id, CreditGrantUpdateParams params) throws StripeException { + return update(id, params, (RequestOptions) null); + } + /** Updates a credit grant. */ + public CreditGrant update(String id, RequestOptions options) throws StripeException { + return update(id, (CreditGrantUpdateParams) null, options); + } + /** Updates a credit grant. */ + public CreditGrant update(String id) throws StripeException { + return update(id, (CreditGrantUpdateParams) null, (RequestOptions) null); + } + /** Updates a credit grant. */ + public CreditGrant update(String id, CreditGrantUpdateParams params, RequestOptions options) + throws StripeException { + String path = String.format("/v1/billing/credit_grants/%s", ApiResource.urlEncodeId(id)); + ApiRequest request = + new ApiRequest( + BaseAddress.API, + ApiResource.RequestMethod.POST, + path, + ApiRequestParams.paramsToMap(params), + options); + return this.request(request, CreditGrant.class); + } + /** Expires a credit grant. */ + public CreditGrant expire(String id, CreditGrantExpireParams params) throws StripeException { + return expire(id, params, (RequestOptions) null); + } + /** Expires a credit grant. */ + public CreditGrant expire(String id, RequestOptions options) throws StripeException { + return expire(id, (CreditGrantExpireParams) null, options); + } + /** Expires a credit grant. */ + public CreditGrant expire(String id) throws StripeException { + return expire(id, (CreditGrantExpireParams) null, (RequestOptions) null); + } + /** Expires a credit grant. */ + public CreditGrant expire(String id, CreditGrantExpireParams params, RequestOptions options) + throws StripeException { + String path = String.format("/v1/billing/credit_grants/%s/expire", ApiResource.urlEncodeId(id)); + ApiRequest request = + new ApiRequest( + BaseAddress.API, + ApiResource.RequestMethod.POST, + path, + ApiRequestParams.paramsToMap(params), + options); + return this.request(request, CreditGrant.class); + } + /** Voids a credit grant. */ + public CreditGrant voidGrant(String id, CreditGrantVoidGrantParams params) + throws StripeException { + return voidGrant(id, params, (RequestOptions) null); + } + /** Voids a credit grant. */ + public CreditGrant voidGrant(String id, RequestOptions options) throws StripeException { + return voidGrant(id, (CreditGrantVoidGrantParams) null, options); + } + /** Voids a credit grant. */ + public CreditGrant voidGrant(String id) throws StripeException { + return voidGrant(id, (CreditGrantVoidGrantParams) null, (RequestOptions) null); + } + /** Voids a credit grant. */ + public CreditGrant voidGrant(String id, CreditGrantVoidGrantParams params, RequestOptions options) + throws StripeException { + String path = String.format("/v1/billing/credit_grants/%s/void", ApiResource.urlEncodeId(id)); + ApiRequest request = + new ApiRequest( + BaseAddress.API, + ApiResource.RequestMethod.POST, + path, + ApiRequestParams.paramsToMap(params), + options); + return this.request(request, CreditGrant.class); + } +} diff --git a/src/main/java/com/stripe/service/v2/BillingService.java b/src/main/java/com/stripe/service/v2/BillingService.java new file mode 100644 index 00000000000..9c33a8697d1 --- /dev/null +++ b/src/main/java/com/stripe/service/v2/BillingService.java @@ -0,0 +1,27 @@ +// File generated from our OpenAPI spec +package com.stripe.service.v2; + +import com.stripe.net.ApiService; +import com.stripe.net.StripeResponseGetter; + +public final class BillingService extends ApiService { + public BillingService(StripeResponseGetter responseGetter) { + super(responseGetter); + } + + public com.stripe.service.v2.billing.MeterEventAdjustmentService meterEventAdjustments() { + return new com.stripe.service.v2.billing.MeterEventAdjustmentService(this.getResponseGetter()); + } + + public com.stripe.service.v2.billing.MeterEventSessionService meterEventSession() { + return new com.stripe.service.v2.billing.MeterEventSessionService(this.getResponseGetter()); + } + + public com.stripe.service.v2.billing.MeterEventStreamService meterEventStream() { + return new com.stripe.service.v2.billing.MeterEventStreamService(this.getResponseGetter()); + } + + public com.stripe.service.v2.billing.MeterEventService meterEvents() { + return new com.stripe.service.v2.billing.MeterEventService(this.getResponseGetter()); + } +} diff --git a/src/main/java/com/stripe/service/v2/CoreService.java b/src/main/java/com/stripe/service/v2/CoreService.java new file mode 100644 index 00000000000..313e025c51c --- /dev/null +++ b/src/main/java/com/stripe/service/v2/CoreService.java @@ -0,0 +1,15 @@ +// File generated from our OpenAPI spec +package com.stripe.service.v2; + +import com.stripe.net.ApiService; +import com.stripe.net.StripeResponseGetter; + +public final class CoreService extends ApiService { + public CoreService(StripeResponseGetter responseGetter) { + super(responseGetter); + } + + public com.stripe.service.v2.core.EventService events() { + return new com.stripe.service.v2.core.EventService(this.getResponseGetter()); + } +} diff --git a/src/main/java/com/stripe/service/v2/billing/MeterEventAdjustmentService.java b/src/main/java/com/stripe/service/v2/billing/MeterEventAdjustmentService.java new file mode 100644 index 00000000000..af09098b89d --- /dev/null +++ b/src/main/java/com/stripe/service/v2/billing/MeterEventAdjustmentService.java @@ -0,0 +1,38 @@ +// File generated from our OpenAPI spec +package com.stripe.service.v2.billing; + +import com.stripe.exception.StripeException; +import com.stripe.model.v2.billing.MeterEventAdjustment; +import com.stripe.net.ApiRequest; +import com.stripe.net.ApiRequestParams; +import com.stripe.net.ApiResource; +import com.stripe.net.ApiService; +import com.stripe.net.BaseAddress; +import com.stripe.net.RequestOptions; +import com.stripe.net.StripeResponseGetter; +import com.stripe.param.v2.billing.MeterEventAdjustmentCreateParams; + +public final class MeterEventAdjustmentService extends ApiService { + public MeterEventAdjustmentService(StripeResponseGetter responseGetter) { + super(responseGetter); + } + + /** Creates a meter event adjustment to cancel a previously sent meter event. */ + public MeterEventAdjustment create(MeterEventAdjustmentCreateParams params) + throws StripeException { + return create(params, (RequestOptions) null); + } + /** Creates a meter event adjustment to cancel a previously sent meter event. */ + public MeterEventAdjustment create( + MeterEventAdjustmentCreateParams params, RequestOptions options) throws StripeException { + String path = "/v2/billing/meter_event_adjustments"; + ApiRequest request = + new ApiRequest( + BaseAddress.API, + ApiResource.RequestMethod.POST, + path, + ApiRequestParams.paramsToMap(params), + options); + return this.request(request, MeterEventAdjustment.class); + } +} diff --git a/src/main/java/com/stripe/service/v2/billing/MeterEventService.java b/src/main/java/com/stripe/service/v2/billing/MeterEventService.java new file mode 100644 index 00000000000..509c97d96df --- /dev/null +++ b/src/main/java/com/stripe/service/v2/billing/MeterEventService.java @@ -0,0 +1,45 @@ +// File generated from our OpenAPI spec +package com.stripe.service.v2.billing; + +import com.stripe.exception.StripeException; +import com.stripe.model.v2.billing.MeterEvent; +import com.stripe.net.ApiRequest; +import com.stripe.net.ApiRequestParams; +import com.stripe.net.ApiResource; +import com.stripe.net.ApiService; +import com.stripe.net.BaseAddress; +import com.stripe.net.RequestOptions; +import com.stripe.net.StripeResponseGetter; +import com.stripe.param.v2.billing.MeterEventCreateParams; + +public final class MeterEventService extends ApiService { + public MeterEventService(StripeResponseGetter responseGetter) { + super(responseGetter); + } + + /** + * Creates a meter event. Events are validated synchronously, but are processed asynchronously. + * Supports up to 1,000 events per second in livemode. For higher rate-limits, please use meter + * event streams instead. + */ + public MeterEvent create(MeterEventCreateParams params) throws StripeException { + return create(params, (RequestOptions) null); + } + /** + * Creates a meter event. Events are validated synchronously, but are processed asynchronously. + * Supports up to 1,000 events per second in livemode. For higher rate-limits, please use meter + * event streams instead. + */ + public MeterEvent create(MeterEventCreateParams params, RequestOptions options) + throws StripeException { + String path = "/v2/billing/meter_events"; + ApiRequest request = + new ApiRequest( + BaseAddress.API, + ApiResource.RequestMethod.POST, + path, + ApiRequestParams.paramsToMap(params), + options); + return this.request(request, MeterEvent.class); + } +} diff --git a/src/main/java/com/stripe/service/v2/billing/MeterEventSessionService.java b/src/main/java/com/stripe/service/v2/billing/MeterEventSessionService.java new file mode 100644 index 00000000000..31658d1ebc0 --- /dev/null +++ b/src/main/java/com/stripe/service/v2/billing/MeterEventSessionService.java @@ -0,0 +1,37 @@ +// File generated from our OpenAPI spec +package com.stripe.service.v2.billing; + +import com.stripe.exception.StripeException; +import com.stripe.model.v2.billing.MeterEventSession; +import com.stripe.net.ApiRequest; +import com.stripe.net.ApiResource; +import com.stripe.net.ApiService; +import com.stripe.net.BaseAddress; +import com.stripe.net.RequestOptions; +import com.stripe.net.StripeResponseGetter; + +public final class MeterEventSessionService extends ApiService { + public MeterEventSessionService(StripeResponseGetter responseGetter) { + super(responseGetter); + } + + /** + * Creates a meter event session to send usage on the high-throughput meter event stream. + * Authentication tokens are only valid for 15 minutes, so you will need to create a new meter + * event session when your token expires. + */ + public MeterEventSession create() throws StripeException { + return create((RequestOptions) null); + } + /** + * Creates a meter event session to send usage on the high-throughput meter event stream. + * Authentication tokens are only valid for 15 minutes, so you will need to create a new meter + * event session when your token expires. + */ + public MeterEventSession create(RequestOptions options) throws StripeException { + String path = "/v2/billing/meter_event_session"; + ApiRequest request = + new ApiRequest(BaseAddress.API, ApiResource.RequestMethod.POST, path, null, options); + return this.request(request, MeterEventSession.class); + } +} diff --git a/src/main/java/com/stripe/service/v2/billing/MeterEventStreamService.java b/src/main/java/com/stripe/service/v2/billing/MeterEventStreamService.java new file mode 100644 index 00000000000..81130f135f8 --- /dev/null +++ b/src/main/java/com/stripe/service/v2/billing/MeterEventStreamService.java @@ -0,0 +1,47 @@ +// File generated from our OpenAPI spec +package com.stripe.service.v2.billing; + +import com.stripe.exception.StripeException; +import com.stripe.exception.TemporarySessionExpiredException; +import com.stripe.net.ApiRequest; +import com.stripe.net.ApiRequestParams; +import com.stripe.net.ApiResource; +import com.stripe.net.ApiService; +import com.stripe.net.BaseAddress; +import com.stripe.net.RequestOptions; +import com.stripe.net.StripeResponseGetter; +import com.stripe.param.v2.billing.MeterEventStreamCreateParams; +import com.stripe.v2.EmptyStripeObject; + +public final class MeterEventStreamService extends ApiService { + public MeterEventStreamService(StripeResponseGetter responseGetter) { + super(responseGetter); + } + + /** + * Creates meter events. Events are processed asynchronously, including validation. Requires a + * meter event session for authentication. Supports up to 10,000 requests per second in livemode. + * For even higher rate-limits, contact sales. + */ + public void create(MeterEventStreamCreateParams params) + throws StripeException, TemporarySessionExpiredException { + create(params, (RequestOptions) null); + } + /** + * Creates meter events. Events are processed asynchronously, including validation. Requires a + * meter event session for authentication. Supports up to 10,000 requests per second in livemode. + * For even higher rate-limits, contact sales. + */ + public void create(MeterEventStreamCreateParams params, RequestOptions options) + throws StripeException, TemporarySessionExpiredException { + String path = "/v2/billing/meter_event_stream"; + ApiRequest request = + new ApiRequest( + BaseAddress.METER_EVENTS, + ApiResource.RequestMethod.POST, + path, + ApiRequestParams.paramsToMap(params), + options); + this.request(request, EmptyStripeObject.class); + } +} diff --git a/src/main/java/com/stripe/service/v2/core/EventService.java b/src/main/java/com/stripe/service/v2/core/EventService.java new file mode 100644 index 00000000000..5732a916f2f --- /dev/null +++ b/src/main/java/com/stripe/service/v2/core/EventService.java @@ -0,0 +1,50 @@ +// File generated from our OpenAPI spec +package com.stripe.service.v2.core; + +import com.google.gson.reflect.TypeToken; +import com.stripe.exception.StripeException; +import com.stripe.model.v2.Event; +import com.stripe.model.v2.StripeCollection; +import com.stripe.net.ApiRequest; +import com.stripe.net.ApiRequestParams; +import com.stripe.net.ApiResource; +import com.stripe.net.ApiService; +import com.stripe.net.BaseAddress; +import com.stripe.net.RequestOptions; +import com.stripe.net.StripeResponseGetter; +import com.stripe.param.v2.core.EventListParams; + +public final class EventService extends ApiService { + public EventService(StripeResponseGetter responseGetter) { + super(responseGetter); + } + + /** List events, going back up to 30 days. */ + public StripeCollection list(EventListParams params) throws StripeException { + return list(params, (RequestOptions) null); + } + /** List events, going back up to 30 days. */ + public StripeCollection list(EventListParams params, RequestOptions options) + throws StripeException { + String path = "/v2/core/events"; + ApiRequest request = + new ApiRequest( + BaseAddress.API, + ApiResource.RequestMethod.GET, + path, + ApiRequestParams.paramsToMap(params), + options); + return this.request(request, new TypeToken>() {}.getType()); + } + /** Retrieves the details of an event. */ + public Event retrieve(String id) throws StripeException { + return retrieve(id, (RequestOptions) null); + } + /** Retrieves the details of an event. */ + public Event retrieve(String id, RequestOptions options) throws StripeException { + String path = String.format("/v2/core/events/%s", ApiResource.urlEncodeId(id)); + ApiRequest request = + new ApiRequest(BaseAddress.API, ApiResource.RequestMethod.GET, path, null, options); + return this.request(request, Event.class); + } +} diff --git a/src/main/java/com/stripe/v2/Amount.java b/src/main/java/com/stripe/v2/Amount.java new file mode 100644 index 00000000000..b2d0a879131 --- /dev/null +++ b/src/main/java/com/stripe/v2/Amount.java @@ -0,0 +1,22 @@ +// NOT codegenned +package com.stripe.v2; + +import com.google.gson.annotations.SerializedName; +import com.stripe.model.StripeObject; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +@Getter +@EqualsAndHashCode(callSuper = false) +public final class Amount extends StripeObject { + public Amount(long value, String currency) { + this.value = value; + this.currency = currency; + } + + @SerializedName("value") + long value; + + @SerializedName("currency") + String currency; +} diff --git a/src/main/java/com/stripe/v2/EmptyStripeObject.java b/src/main/java/com/stripe/v2/EmptyStripeObject.java new file mode 100644 index 00000000000..19e49ad60b9 --- /dev/null +++ b/src/main/java/com/stripe/v2/EmptyStripeObject.java @@ -0,0 +1,10 @@ +package com.stripe.v2; + +import com.stripe.model.StripeObject; + +/** + * An empty entity for API methods with a void return type. We need a class to to deserialize into, + * but we can't instantiate the abstract `StripeObject` directly. This class shouldn't do anything. + * It's handwritten, not auto-generated. + */ +public final class EmptyStripeObject extends StripeObject {} diff --git a/src/test/java/com/stripe/BaseStripeTest.java b/src/test/java/com/stripe/BaseStripeTest.java index 271b20da3f6..9e65357b47e 100644 --- a/src/test/java/com/stripe/BaseStripeTest.java +++ b/src/test/java/com/stripe/BaseStripeTest.java @@ -47,6 +47,8 @@ public class BaseStripeTest { private String origClientId; private String origUploadBase; + protected static final String TEST_API_KEY = "sk_test_123"; + static { // To only stop stripe-mock process after all the test classes. // Alternative solution using @AfterClass will stop the stripe-mock after @@ -125,7 +127,8 @@ public void setUpStripeMockUsage() { httpClientSpy = Mockito.spy(new HttpURLConnectionClient()); networkSpy = Mockito.spy(new LiveStripeResponseGetter(null, httpClientSpy)); mockClient = new StripeClient(networkSpy); - ApiResource.setStripeResponseGetter(networkSpy); + + ApiResource.setGlobalResponseGetter(networkSpy); OAuth.setGlobalResponseGetter(networkSpy); } @@ -135,7 +138,7 @@ public void setUpStripeMockUsage() { */ @AfterEach public void tearDownStripeMockUsage() { - ApiResource.setStripeResponseGetter(new LiveStripeResponseGetter()); + ApiResource.setGlobalResponseGetter(new LiveStripeResponseGetter()); Stripe.overrideApiBase(this.origApiBase); Stripe.overrideUploadBase(this.origUploadBase); @@ -313,7 +316,7 @@ public static void stubRequest( * @param path request path (e.g. "/v1/charges"). Can also be an abolute URL. * @param params map containing the parameters. If null, the parameters are not checked. * @param options request options. If null, the options are not checked. - * @param clazz Class of the API resource that will be returned for the stubbed request. + * @param typeToken Class of the API resource that will be returned for the stubbed request. * @param response JSON payload of the API resource that will be returned for the stubbed request. */ public static void stubRequest( diff --git a/src/test/java/com/stripe/DocumentationTest.java b/src/test/java/com/stripe/DocumentationTest.java index ac0efba4c7d..aa1e6a35235 100644 --- a/src/test/java/com/stripe/DocumentationTest.java +++ b/src/test/java/com/stripe/DocumentationTest.java @@ -1,10 +1,8 @@ package com.stripe; -import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import com.google.common.base.Joiner; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; @@ -12,10 +10,7 @@ import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; -import java.util.ArrayList; import java.util.Calendar; -import java.util.List; -import java.util.regex.Pattern; import org.junit.jupiter.api.Test; public class DocumentationTest { @@ -28,90 +23,6 @@ private static String formatDateTime() { return result; } - @Test - public void testChangeLogContainsStaticVersion() throws IOException { - final File changelogFile = new File("CHANGELOG.md").getAbsoluteFile(); - - assertTrue( - changelogFile.exists(), - String.format( - "Expected CHANGELOG file to exist, but it doesn't. (path is %s).", - changelogFile.getAbsolutePath())); - assertTrue( - changelogFile.isFile(), - String.format( - "Expected CHANGELOG to be a file, but it isn't. (path is %s).", - changelogFile.getAbsolutePath())); - - try (final BufferedReader reader = - new BufferedReader( - new InputStreamReader(new FileInputStream(changelogFile), StandardCharsets.UTF_8))) { - final String expectedLine = formatDateTime(); - final String pattern = - String.format( - "^## %s - 20[12][0-9]-(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-9]|3[0-1])$", - Stripe.VERSION); - final List closeMatches = new ArrayList(); - String line; - - while ((line = reader.readLine()) != null) { - if (line.contains(Stripe.VERSION)) { - if (Pattern.matches(pattern, line)) { - return; - } - closeMatches.add(line); - } - } - - fail( - String.format( - "Expected a line of the format '%s' in the CHANGELOG, but didn't find one.%n" - + "The following lines were close, but didn't match exactly:%n'%s'", - expectedLine, Joiner.on(", ").join(closeMatches))); - } - } - - @Test - public void testReadMeContainsStripeVersionThatMatches() throws IOException { - // this will be very flaky, but we want to ensure that the readme is correct. - final File readmeFile = new File("README.md").getAbsoluteFile(); - - assertTrue( - readmeFile.exists(), - String.format( - "Expected README.md file to exist, but it doesn't. (path is %s).", - readmeFile.getAbsolutePath())); - assertTrue( - readmeFile.isFile(), - String.format( - "Expected README.md to be a file, but it doesn't. (path is %s).", - readmeFile.getAbsolutePath())); - - try (final BufferedReader reader = - new BufferedReader( - new InputStreamReader(new FileInputStream(readmeFile), StandardCharsets.UTF_8))) { - final int expectedMentionsOfVersion = 4; - // Currently three places mention the Stripe version: the latest Maven JAR hyperlink, the - // sample pom, and gradle files. - final List mentioningLines = new ArrayList(); - String line; - - while ((line = reader.readLine()) != null) { - if (line.contains(Stripe.VERSION)) { - mentioningLines.add(line); - } - } - - final String message = - String.format( - "Expected %d mentions of the stripe-java version in the Readme, but found %d:%n%s", - expectedMentionsOfVersion, - mentioningLines.size(), - Joiner.on(", ").join(mentioningLines)); - assertSame(expectedMentionsOfVersion, mentioningLines.size(), message); - } - } - @Test public void testGradlePropertiesContainsVersionThatMatches() throws IOException { // we want to ensure that the pom's version matches the static version. diff --git a/src/test/java/com/stripe/RawRequestTest.java b/src/test/java/com/stripe/RawRequestTest.java new file mode 100644 index 00000000000..91643254aac --- /dev/null +++ b/src/test/java/com/stripe/RawRequestTest.java @@ -0,0 +1,336 @@ +package com.stripe; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.stripe.exception.StripeException; +import com.stripe.model.Customer; +import com.stripe.model.v2.billing.MeterEvent; +import com.stripe.net.*; +import com.stripe.net.ApiResource.RequestMethod; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class RawRequestTest extends BaseStripeTest { + private static MockWebServer server; + private static StripeClient client; + private StripeResponseGetter responseGetter; + + @BeforeEach + void setUp() throws IOException { + server = new MockWebServer(); + server.start(); + Stripe.overrideApiBase(server.url("").toString()); + responseGetter = + Mockito.spy( + new LiveStripeResponseGetter( + StripeClient.builder() + .setApiKey(TEST_API_KEY) + .setApiBase(server.url("").toString()) + .buildOptions(), + new HttpURLConnectionClient())); + client = new StripeClient(responseGetter); + } + + @AfterEach + void tearDown() throws IOException { + server.shutdown(); + } + + @Test + public void testStandardRequestGlobal() throws StripeException, InterruptedException { + server.enqueue( + new MockResponse() + .setBody( + "{\"id\": \"cus_123\",\n \"object\": \"customer\",\n \"description\": \"test customer\"}")); + + final RawRequestOptions options = RawRequestOptions.builder().setApiKey("sk_123").build(); + + final StripeResponse response = + client.rawRequest( + RequestMethod.POST, "/v1/customers", "description=test+customer", options); + + assertNotNull(response); + assertEquals(200, response.code()); + assertTrue(response.body().length() > 0); + + RecordedRequest request = server.takeRequest(); + assertEquals( + "application/x-www-form-urlencoded;charset=UTF-8", request.getHeader("Content-Type")); + assertEquals(Stripe.API_VERSION, request.getHeader("Stripe-Version")); + assertEquals("description=test+customer", request.getBody().readUtf8()); + } + + @Test + public void testNullOptionsGlobal() throws StripeException, InterruptedException { + server.enqueue(new MockResponse().setBody("{}")); + final StripeResponse response = + client.rawRequest(RequestMethod.POST, "/v1/customers", "description=test+customer", null); + assertNotNull(response); + } + + @Test + public void testV2PostRequestGlobal() throws StripeException, InterruptedException { + server.enqueue( + new MockResponse() + .setBody("{\"id\": \"sub_sched_123\",\n \"object\": \"subscription_schedule\"}")); + final RawRequestOptions options = RawRequestOptions.builder().setApiKey("sk_123").build(); + + final StripeResponse response = + client.rawRequest( + RequestMethod.POST, "/v2/core/event", "{\"event_id\": \"evnt_123\"}", options); + + RecordedRequest request = server.takeRequest(); + assertEquals("application/json", request.getHeader("Content-Type")); + assertEquals(Stripe.API_VERSION, request.getHeader("Stripe-Version")); + assertEquals("{\"event_id\": \"evnt_123\"}", request.getBody().readUtf8()); + + assertNotNull(response); + assertEquals(200, response.code()); + assertTrue(response.body().length() > 0); + } + + @Test + public void testPreviewGetRequestGlobal() throws StripeException, InterruptedException { + server.enqueue( + new MockResponse() + .setBody("{\"id\": \"sub_sched_123\",\n \"object\": \"subscription_schedule\"}")); + final RawRequestOptions options = RawRequestOptions.builder().setApiKey("sk_123").build(); + + final StripeResponse response = + client.rawRequest(RequestMethod.GET, "/v1/subscription_schedules", "", options); + + RecordedRequest request = server.takeRequest(); + assertEquals(null, request.getHeader("Content-Type")); + assertEquals(Stripe.API_VERSION, request.getHeader("Stripe-Version")); + assertEquals("", request.getBody().readUtf8()); + + assertNotNull(response); + assertEquals(200, response.code()); + assertTrue(response.body().length() > 0); + } + + @Test + public void testAdditionalHeadersGlobal() throws StripeException, InterruptedException { + server.enqueue( + new MockResponse() + .setBody( + "{\"id\": \"cus_123\",\n \"object\": \"customer\",\n \"description\": \"test customer\"}")); + + Map additionalHeaders = new HashMap<>(); + + additionalHeaders.put("foo", "bar"); + final RawRequestOptions options = + RawRequestOptions.builder().setAdditionalHeaders(additionalHeaders).build(); + + assertEquals(additionalHeaders, options.getAdditionalHeaders()); + + final StripeResponse response = + client.rawRequest(RequestMethod.GET, "/v1/customers", null, options); + + RecordedRequest request = server.takeRequest(); + assertEquals("bar", request.getHeader("foo")); + + assertNotNull(response); + assertEquals(200, response.code()); + assertTrue(response.body().length() > 0); + } + + @Test + public void testDeserializeGlobal() throws StripeException, InterruptedException { + server.enqueue( + new MockResponse() + .setBody( + "{\"id\": \"cus_123\",\n \"object\": \"customer\",\n \"description\": \"test customer\"}")); + + final RawRequestOptions options = RawRequestOptions.builder().setApiKey("sk_123").build(); + + final StripeResponse response = + client.rawRequest( + RequestMethod.POST, "/v1/customers", "description=test+customer", options); + + assertNotNull(response); + assertEquals(200, response.code()); + assertTrue(response.body().length() > 0); + + Customer customer = (Customer) client.deserialize(response.body(), ApiMode.V1); + assertTrue(customer.getId().startsWith("cus_")); + assertEquals("test customer", customer.getDescription()); + } + + @Test + public void testRaisesErrorWhenGetRequestAndContentIsNonNullGlobal() throws StripeException { + try { + client.rawRequest(RequestMethod.GET, "/v1/customers", "key=value!", null); + fail("Expected illegal argument exception."); + } catch (IllegalArgumentException e) { + assertTrue(e.getMessage().contains("content is not allowed for non-POST requests.")); + } + } + + @Test + public void testNullOptionsClient() throws StripeException, InterruptedException { + server.enqueue(new MockResponse().setBody("{}")); + final StripeResponse response = + client.rawRequest(RequestMethod.POST, "/v1/customers", "description=test+customer", null); + assertNotNull(response); + } + + @Test + public void testV1RequestClient() throws StripeException, InterruptedException { + server.enqueue( + new MockResponse() + .setBody( + "{\"id\": \"cus_123\",\n \"object\": \"customer\",\n \"description\": \"test customer\"}")); + + final RawRequestOptions options = RawRequestOptions.builder().setApiKey("sk_123").build(); + + final StripeResponse response = + client.rawRequest( + RequestMethod.POST, "/v1/customers", "description=test+customer", options); + + assertNotNull(response); + assertEquals(200, response.code()); + assertTrue(response.body().length() > 0); + + RecordedRequest request = server.takeRequest(); + assertEquals( + "application/x-www-form-urlencoded;charset=UTF-8", request.getHeader("Content-Type")); + assertEquals(Stripe.API_VERSION, request.getHeader("Stripe-Version")); + assertEquals("description=test+customer", request.getBody().readUtf8()); + } + + @Test + public void testV2PostRequestClient() throws StripeException, InterruptedException { + server.enqueue( + new MockResponse() + .setBody("{\"id\": \"sub_sched_123\",\n \"object\": \"subscription_schedule\"}")); + final RawRequestOptions options = RawRequestOptions.builder().setApiKey("sk_123").build(); + + final StripeResponse response = + client.rawRequest( + RequestMethod.POST, "/v2/core/events", "{\"event_id\": \"evnt_123\"}", options); + + RecordedRequest request = server.takeRequest(); + assertEquals("application/json", request.getHeader("Content-Type")); + assertEquals(Stripe.API_VERSION, request.getHeader("Stripe-Version")); + assertEquals("{\"event_id\": \"evnt_123\"}", request.getBody().readUtf8()); + + assertNotNull(response); + assertEquals(200, response.code()); + assertTrue(response.body().length() > 0); + } + + @Test + public void testPreviewGetRequestClient() throws StripeException, InterruptedException { + server.enqueue( + new MockResponse() + .setBody("{\"id\": \"sub_sched_123\",\n \"object\": \"subscription_schedule\"}")); + final RawRequestOptions options = RawRequestOptions.builder().setApiKey("sk_123").build(); + + final StripeResponse response = + client.rawRequest(RequestMethod.GET, "/v1/subscription_schedules", "", options); + + RecordedRequest request = server.takeRequest(); + assertEquals(null, request.getHeader("Content-Type")); + assertEquals(Stripe.API_VERSION, request.getHeader("Stripe-Version")); + assertEquals("", request.getBody().readUtf8()); + + assertNotNull(response); + assertEquals(200, response.code()); + assertTrue(response.body().length() > 0); + } + + @Test + public void testAdditionalHeadersClient() throws StripeException, InterruptedException { + server.enqueue( + new MockResponse() + .setBody( + "{\"id\": \"cus_123\",\n \"object\": \"customer\",\n \"description\": \"test customer\"}")); + + Map additionalHeaders = new HashMap<>(); + + additionalHeaders.put("foo", "bar"); + final RawRequestOptions options = + RawRequestOptions.builder().setAdditionalHeaders(additionalHeaders).build(); + + assertEquals(additionalHeaders, options.getAdditionalHeaders()); + + final StripeResponse response = + client.rawRequest(RequestMethod.GET, "/v1/customers", null, options); + + RecordedRequest request = server.takeRequest(); + assertEquals("bar", request.getHeader("foo")); + + assertNotNull(response); + assertEquals(200, response.code()); + assertTrue(response.body().length() > 0); + } + + @Test + public void testDeserializeClientV1Api() throws StripeException, InterruptedException { + server.enqueue( + new MockResponse() + .setBody( + "{\"id\": \"cus_123\",\n \"object\": \"customer\",\n \"description\": \"test customer\"}")); + + final RawRequestOptions options = RawRequestOptions.builder().setApiKey("sk_123").build(); + + final StripeResponse response = + client.rawRequest( + RequestMethod.POST, "/v1/customers", "description=test+customer", options); + + assertNotNull(response); + assertEquals(200, response.code()); + assertTrue(response.body().length() > 0); + + Customer customer = (Customer) client.deserialize(response.body(), ApiMode.V1); + assertTrue(customer.getId().startsWith("cus_")); + assertEquals("test customer", customer.getDescription()); + assertTrue(Mockito.mockingDetails(responseGetter).getInvocations().stream().count() > 0); + } + + @Test + public void testDeserializeClientV2Api() throws StripeException, InterruptedException { + server.enqueue( + new MockResponse() + .setBody( + "{\"object\":\"billing.meter_event\",\"created\":\"2024-10-01T04:46:22.861Z\",\"event_name\":\"new_meter\",\"identifier\":\"d8a5ab2e-81ec-4bdf-acbf-48bf346\",\"livemode\":false,\"payload\":{\"stripe_customer_id\":\"cus_QvF3b2W6\",\"value\":\"25\"},\"timestamp\":\"2024-10-01T04:46:22.836Z\"}")); + + final RawRequestOptions options = RawRequestOptions.builder().setApiKey("sk_123").build(); + String param = + "{\"event_name\":\"new_meter\",\"payload\":{\"stripe_customer_id\":\"cus_QvF3b2W6\",\"value\":\"25\"}}"; + + final StripeResponse response = + client.rawRequest(RequestMethod.POST, "/v2/billing/meter_event", param, options); + + assertNotNull(response); + assertEquals(200, response.code()); + assertTrue(response.body().length() > 0); + + MeterEvent meterEvent = (MeterEvent) client.deserialize(response.body(), ApiMode.V2); + assertEquals("new_meter", meterEvent.getEventName()); + assertEquals("d8a5ab2e-81ec-4bdf-acbf-48bf346", meterEvent.getIdentifier()); + assertTrue(Mockito.mockingDetails(responseGetter).getInvocations().stream().count() > 0); + } + + @Test + public void testRaisesErrorWhenGetRequestAndContentIsNonNullClient() throws StripeException { + try { + client.rawRequest(RequestMethod.GET, "/v1/customers", "key=value!", null); + fail("Expected illegal argument exception."); + } catch (IllegalArgumentException e) { + assertTrue(e.getMessage().contains("content is not allowed for non-POST requests.")); + } + } +} diff --git a/src/test/java/com/stripe/StripeClientTest.java b/src/test/java/com/stripe/StripeClientTest.java index f10573da3b4..88bc380a8e5 100644 --- a/src/test/java/com/stripe/StripeClientTest.java +++ b/src/test/java/com/stripe/StripeClientTest.java @@ -1,16 +1,90 @@ package com.stripe; +import static org.junit.Assert.assertThrows; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.stripe.exception.SignatureVerificationException; +import com.stripe.exception.StripeException; +import com.stripe.model.ThinEvent; import com.stripe.model.terminal.Reader; import com.stripe.net.*; import java.lang.reflect.Type; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.mockito.stubbing.Answer; public class StripeClientTest extends BaseStripeTest { + private Boolean originalTelemetry; + + @BeforeEach + public void setUp() { + this.originalTelemetry = Stripe.enableTelemetry; + Stripe.enableTelemetry = true; + } + + @AfterEach + public void tearDown() { + Stripe.enableTelemetry = originalTelemetry; + } + + @Test + public void testReportsStripeClientUsageTelemetry() throws StripeException { + mockClient.customers().create(); + mockClient.customers().update("cus_xyz"); + verifyStripeRequest( + (stripeRequest) -> { + assert (stripeRequest.headers().firstValue("X-Stripe-Client-Telemetry").isPresent()); + String usage = + new Gson() + .fromJson( + stripeRequest.headers().firstValue("X-Stripe-Client-Telemetry").get(), + JsonObject.class) + .get("last_request_metrics") + .getAsJsonObject() + .get("usage") + .getAsString(); + assertEquals("stripe_client", usage); + }); + } + + // TODO: https://go/j/DEVSDK-2178 + // @Test + public void testReportsRawRequestUsageTelemetry() throws StripeException { + mockClient.rawRequest( + com.stripe.net.ApiResource.RequestMethod.POST, "/v1/customers", "description=foo", null); + mockClient.rawRequest( + com.stripe.net.ApiResource.RequestMethod.POST, "/v1/customers", "description=foo", null); + verifyStripeRequest( + (stripeRequest) -> { + assert (stripeRequest.headers().firstValue("X-Stripe-Client-Telemetry").isPresent()); + JsonArray usage = + new Gson() + .fromJson( + stripeRequest.headers().firstValue("X-Stripe-Client-Telemetry").get(), + JsonObject.class) + .get("last_request_metrics") + .getAsJsonObject() + .get("usage") + .getAsJsonArray(); + assertEquals(2, usage.size()); + assertEquals("stripe_client", usage.get(0).getAsString()); + assertEquals("raw_request", usage.get(1).getAsString()); + }); + } + @Test public void testFlowsStripeResponseGetter() throws Exception { StripeResponseGetter responseGetter = Mockito.spy(new LiveStripeResponseGetter()); @@ -34,6 +108,113 @@ public void clientOptionsDefaults() { assertEquals(Stripe.LIVE_API_BASE, options.getApiBase()); assertEquals(Stripe.CONNECT_API_BASE, options.getConnectBase()); assertEquals(Stripe.UPLOAD_API_BASE, options.getFilesBase()); + assertEquals(Stripe.METER_EVENTS_API_BASE, options.getMeterEventsBase()); assertEquals(0, options.getMaxNetworkRetries()); } + + @Test + public void checksWebhookSignature() + throws InvalidKeyException, NoSuchAlgorithmException, SignatureVerificationException { + StripeClient client = new StripeClient("sk_123"); + + String payload = "{\n \"id\": \"evt_test_webhook\",\n \"object\": \"event\"\n}"; + String secret = "whsec_test_secret"; + + Map options = new HashMap<>(); + options.put("payload", payload); + options.put("secret", secret); + + String signature = WebhookTest.generateSigHeader(options); + + ThinEvent e = client.parseThinEvent(payload, signature, secret); + assertEquals(e.getId(), "evt_test_webhook"); + } + + @Test + public void failsWebhookVerification() + throws InvalidKeyException, NoSuchAlgorithmException, SignatureVerificationException { + StripeClient client = new StripeClient("sk_123"); + + String payload = "{\n \"id\": \"evt_test_webhook\",\n \"object\": \"event\"\n}"; + String secret = "whsec_test_secret"; + String signature = "bad signature"; + + assertThrows( + SignatureVerificationException.class, + () -> { + client.parseThinEvent(payload, signature, secret); + }); + } + + static final String v2PushEventWithRelatedObject = + "{\n" + + " \"id\": \"evt_234\",\n" + + " \"object\": \"event\",\n" + + " \"type\": \"financial_account.balance.opened\",\n" + + " \"created\": \"2022-02-15T00:27:45.330Z\",\n" + + " \"context\": \"context 123\",\n" + + " \"related_object\": {\n" + + " \"id\": \"fa_123\",\n" + + " \"type\": \"financial_account\",\n" + + " \"url\": \"/v2/financial_accounts/fa_123\",\n" + + " \"stripe_context\": \"acct_123\"\n" + + " }\n" + + "}"; + + static final String v2PushEventWithoutRelatedObject = + "{\n" + + " \"id\": \"evt_234\",\n" + + " \"object\": \"event\",\n" + + " \"type\": \"financial_account.balance.opened\",\n" + + " \"created\": \"2022-02-15T00:27:45.330Z\"\n" + + "}"; + + @Test + public void parsesThinEventWithoutRelatedObject() + throws InvalidKeyException, NoSuchAlgorithmException, SignatureVerificationException { + + StripeClient client = new StripeClient("sk_123"); + + String secret = "whsec_test_secret"; + + Map options = new HashMap<>(); + options.put("payload", v2PushEventWithoutRelatedObject); + options.put("secret", secret); + + String signature = WebhookTest.generateSigHeader(options); + ThinEvent baseThinEvent = + client.parseThinEvent(v2PushEventWithoutRelatedObject, signature, secret); + assertNotNull(baseThinEvent); + assertEquals("evt_234", baseThinEvent.getId()); + assertEquals("financial_account.balance.opened", baseThinEvent.getType()); + assertEquals(Instant.parse("2022-02-15T00:27:45.330Z"), baseThinEvent.created); + assertNull(baseThinEvent.context); + assertNull(baseThinEvent.relatedObject); + } + + @Test + public void parsesThinEventWithRelatedObject() + throws InvalidKeyException, NoSuchAlgorithmException, SignatureVerificationException { + + StripeClient client = new StripeClient("sk_123"); + + String secret = "whsec_test_secret"; + + Map options = new HashMap<>(); + options.put("payload", v2PushEventWithRelatedObject); + options.put("secret", secret); + + String signature = WebhookTest.generateSigHeader(options); + ThinEvent baseThinEvent = + client.parseThinEvent(v2PushEventWithRelatedObject, signature, secret); + assertNotNull(baseThinEvent); + assertEquals("evt_234", baseThinEvent.getId()); + assertEquals("financial_account.balance.opened", baseThinEvent.getType()); + assertEquals(Instant.parse("2022-02-15T00:27:45.330Z"), baseThinEvent.created); + assertEquals("context 123", baseThinEvent.context); + assertNotNull(baseThinEvent.relatedObject); + assertEquals("fa_123", baseThinEvent.relatedObject.id); + assertEquals("financial_account", baseThinEvent.relatedObject.type); + assertEquals("/v2/financial_accounts/fa_123", baseThinEvent.relatedObject.url); + } } diff --git a/src/test/java/com/stripe/functional/ErrorTest.java b/src/test/java/com/stripe/functional/ErrorTest.java index c4b77c3965f..e461a124a0e 100644 --- a/src/test/java/com/stripe/functional/ErrorTest.java +++ b/src/test/java/com/stripe/functional/ErrorTest.java @@ -1,17 +1,14 @@ package com.stripe.functional; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.*; import com.stripe.BaseStripeTest; import com.stripe.Stripe; -import com.stripe.exception.InvalidRequestException; -import com.stripe.exception.StripeException; +import com.stripe.exception.*; import com.stripe.exception.oauth.InvalidClientException; import com.stripe.model.Balance; -import com.stripe.net.HttpHeaders; -import com.stripe.net.OAuth; -import com.stripe.net.StripeResponse; +import com.stripe.model.StripeError; +import com.stripe.net.*; import java.io.IOException; import java.util.Collections; import lombok.Cleanup; @@ -47,6 +44,91 @@ public void testStripeError() throws StripeException, IOException, InterruptedEx assertNotNull(exception.getStripeError().getLastResponse()); } + @Test + public void testV2OutboundPaymentInsufficientFundsError() + throws StripeException, IOException, InterruptedException { + TemporarySessionExpiredException exception = null; + @Cleanup MockWebServer server = new MockWebServer(); + Mockito.doAnswer( + (Answer) + invocation -> + new StripeResponse( + 400, + HttpHeaders.of(Collections.emptyMap()), + getResourceAsString( + "/api_fixtures/error_v2_outbound_payment_insufficient_funds.json"))) + .when(httpClientSpy) + .request(Mockito.any()); + + Stripe.overrideApiBase(server.url("").toString()); + + try { + mockClient.v2().core().events().retrieve("event_123"); + } catch (TemporarySessionExpiredException e) { + exception = e; + } + + assertNotNull(exception); + assertInstanceOf(TemporarySessionExpiredException.class, exception); + assertInstanceOf(com.stripe.model.StripeError.class, exception.getStripeError()); + assertEquals("Session expired", exception.getStripeError().getMessage()); + } + + @Test + public void testV2InvalidErrorEmpty() throws StripeException, IOException, InterruptedException { + ApiException exception = null; + @Cleanup MockWebServer server = new MockWebServer(); + Mockito.doAnswer( + (Answer) + invocation -> new StripeResponse(404, HttpHeaders.of(Collections.emptyMap()), "{}")) + .when(httpClientSpy) + .request(Mockito.any()); + + Stripe.overrideApiBase(server.url("").toString()); + + try { + mockClient.v2().core().events().retrieve("event_123"); + } catch (ApiException e) { + exception = e; + } + + assertNotNull(exception); + assertInstanceOf(ApiException.class, exception); + assertNull(exception.getStripeError()); + assertNull(exception.getUserMessage()); + assertEquals("Unrecognized error type ''; code: ", exception.getMessage()); + } + + @Test + public void testV2UnknownExceptionValidError() + throws StripeException, IOException, InterruptedException { + ApiException exception = null; + @Cleanup MockWebServer server = new MockWebServer(); + Mockito.doAnswer( + (Answer) + invocation -> + new StripeResponse( + 400, + HttpHeaders.of(Collections.emptyMap()), + "{\"error\": {\"type\": \"ceci_nest_pas_une_error_type\", \"code\": \"some_error_code\", \"message\": \"good luck debugging this one\"}}")) + .when(httpClientSpy) + .request(Mockito.any()); + + Stripe.overrideApiBase(server.url("").toString()); + + try { + mockClient.v2().core().events().retrieve("event_123"); + } catch (ApiException e) { + exception = e; + } + + assertNotNull(exception); + assertInstanceOf(ApiException.class, exception); + assertInstanceOf(StripeError.class, exception.getStripeError()); + assertNull(exception.getUserMessage()); + assertEquals("good luck debugging this one; code: some_error_code", exception.getMessage()); + } + @Test public void testOAuthError() throws StripeException, IOException, InterruptedException { String oldBase = Stripe.getConnectBase(); diff --git a/src/test/java/com/stripe/functional/GeneratedExamples.java b/src/test/java/com/stripe/functional/GeneratedExamples.java index 4113c48a1ac..1901347e306 100644 --- a/src/test/java/com/stripe/functional/GeneratedExamples.java +++ b/src/test/java/com/stripe/functional/GeneratedExamples.java @@ -1995,6 +1995,24 @@ public void testCheckoutSessionsPost2Services() throws StripeException { null); } + @Test + public void testCoreEventsGetServices() throws StripeException { + stubRequest( + BaseAddress.API, + ApiResource.RequestMethod.GET, + "/v2/core/events/ll_123", + null, + null, + com.stripe.model.v2.Event.class, + "{\"context\":\"context\",\"created\":\"1970-01-12T21:42:34.472Z\",\"id\":\"obj_123\",\"livemode\":true,\"object\":\"v2.core.event\",\"reason\":{\"type\":\"request\",\"request\":{\"id\":\"obj_123\",\"idempotency_key\":\"idempotency_key\"}},\"type\":\"type\"}"); + StripeClient client = new StripeClient(networkSpy); + + com.stripe.model.v2.Event event = client.v2().core().events().retrieve("ll_123"); + assertNotNull(event); + verifyRequest( + BaseAddress.API, ApiResource.RequestMethod.GET, "/v2/core/events/ll_123", null, null); + } + @Test public void testCountrySpecsGet() throws StripeException { CountrySpecListParams params = CountrySpecListParams.builder().setLimit(3L).build(); @@ -11888,48 +11906,6 @@ public void testTerminalReadersProcessPaymentIntentPostServices() throws StripeE null); } - @Test - public void testTerminalReadersProcessSetupIntentPost() throws StripeException { - com.stripe.model.terminal.Reader resource = - com.stripe.model.terminal.Reader.retrieve("tmr_xxxxxxxxxxxxx"); - - com.stripe.param.terminal.ReaderProcessSetupIntentParams params = - com.stripe.param.terminal.ReaderProcessSetupIntentParams.builder() - .setSetupIntent("seti_xxxxxxxxxxxxx") - .setCustomerConsentCollected(true) - .build(); - - com.stripe.model.terminal.Reader reader = resource.processSetupIntent(params); - assertNotNull(reader); - verifyRequest( - BaseAddress.API, - ApiResource.RequestMethod.POST, - "/v1/terminal/readers/tmr_xxxxxxxxxxxxx/process_setup_intent", - params.toMap(), - null); - } - - @Test - public void testTerminalReadersProcessSetupIntentPostServices() throws StripeException { - StripeClient client = new StripeClient(networkSpy); - - com.stripe.param.terminal.ReaderProcessSetupIntentParams params = - com.stripe.param.terminal.ReaderProcessSetupIntentParams.builder() - .setSetupIntent("seti_xxxxxxxxxxxxx") - .setCustomerConsentCollected(true) - .build(); - - com.stripe.model.terminal.Reader reader = - client.terminal().readers().processSetupIntent("tmr_xxxxxxxxxxxxx", params); - assertNotNull(reader); - verifyRequest( - BaseAddress.API, - ApiResource.RequestMethod.POST, - "/v1/terminal/readers/tmr_xxxxxxxxxxxxx/process_setup_intent", - params.toMap(), - null); - } - @Test public void testTestHelpersCustomersFundCashBalancePost() throws StripeException { Customer resource = Customer.retrieve("cus_123"); diff --git a/src/test/java/com/stripe/functional/LiveStripeResponseGetterTest.java b/src/test/java/com/stripe/functional/LiveStripeResponseGetterTest.java index 143635f1ff6..afc1f623ff4 100644 --- a/src/test/java/com/stripe/functional/LiveStripeResponseGetterTest.java +++ b/src/test/java/com/stripe/functional/LiveStripeResponseGetterTest.java @@ -7,6 +7,7 @@ import com.google.gson.JsonSyntaxException; import com.stripe.BaseStripeTest; import com.stripe.exception.ApiException; +import com.stripe.exception.IdempotencyException; import com.stripe.exception.StripeException; import com.stripe.model.Subscription; import com.stripe.net.ApiResource; @@ -27,7 +28,7 @@ public class LiveStripeResponseGetterTest extends BaseStripeTest { public void testInvalidJson() throws StripeException { HttpClient spy = Mockito.spy(new HttpURLConnectionClient()); StripeResponseGetter srg = new LiveStripeResponseGetter(spy); - ApiResource.setStripeResponseGetter(srg); + ApiResource.setGlobalResponseGetter(srg); StripeResponse response = new StripeResponse(200, HttpHeaders.of(Collections.emptyMap()), "invalid JSON"); Mockito.doReturn(response).when(spy).requestWithRetries(Mockito.any()); @@ -42,4 +43,24 @@ public void testInvalidJson() throws StripeException { assertNotNull(exception.getCause()); assertThat(exception.getCause(), CoreMatchers.instanceOf(JsonSyntaxException.class)); } + + @Test + public void testIdempotencyError() throws StripeException { + HttpClient spy = Mockito.spy(new HttpURLConnectionClient()); + StripeResponseGetter srg = new LiveStripeResponseGetter(spy); + ApiResource.setGlobalResponseGetter(srg); + StripeResponse response = + new StripeResponse( + 400, + HttpHeaders.of(Collections.emptyMap()), + "{\"error\": {\"message\": \"idempotency\", \"type\": \"idempotency_error\"}}"); + Mockito.doReturn(response).when(spy).requestWithRetries(Mockito.any()); + Exception exception = + assertThrows( + IdempotencyException.class, + () -> { + Subscription.retrieve("sub_123"); + }); + assertThat(exception.getMessage(), CoreMatchers.containsString("idempotency")); + } } diff --git a/src/test/java/com/stripe/functional/StripeResponseStreamTest.java b/src/test/java/com/stripe/functional/StripeResponseStreamTest.java index c71517cf87e..b42b444b401 100644 --- a/src/test/java/com/stripe/functional/StripeResponseStreamTest.java +++ b/src/test/java/com/stripe/functional/StripeResponseStreamTest.java @@ -9,7 +9,6 @@ import com.stripe.exception.InvalidRequestException; import com.stripe.exception.StripeException; import com.stripe.model.HasId; -import com.stripe.net.ApiMode; import com.stripe.net.ApiRequest; import com.stripe.net.ApiResource; import com.stripe.net.BaseAddress; @@ -47,12 +46,12 @@ public InputStream pdf() throws StripeException { String url = String.format("/v1/foobars/%s/pdf", ApiResource.urlEncodeId(this.getId())); return getResponseGetter() .requestStream( - BaseAddress.API, - ApiResource.RequestMethod.POST, - url, - (Map) null, - (RequestOptions) null, - ApiMode.V1); + new ApiRequest( + BaseAddress.FILES, + ApiResource.RequestMethod.POST, + url, + (Map) null, + (RequestOptions) null)); } } @@ -65,6 +64,7 @@ public void testStreamedResponseSuccess() server.start(); Stripe.overrideApiBase(server.url("").toString()); + Stripe.overrideUploadBase(server.url("").toString()); TestResource t = TestResource.retrieve("foo_123"); server.takeRequest(); @@ -90,6 +90,7 @@ public void testStreamedResponseFailure() server.start(); Stripe.overrideApiBase(server.url("").toString()); + Stripe.overrideUploadBase(server.url("").toString()); TestResource r = TestResource.retrieve("foo_123"); server.takeRequest(); diff --git a/src/test/java/com/stripe/functional/TimeoutTest.java b/src/test/java/com/stripe/functional/TimeoutTest.java index 07754edb846..870d5ebded7 100644 --- a/src/test/java/com/stripe/functional/TimeoutTest.java +++ b/src/test/java/com/stripe/functional/TimeoutTest.java @@ -22,7 +22,8 @@ public void testReadTimeout() throws IOException, StripeException { new ServerSocket(0, 1, Inet4Address.getByName("localhost"))) { Stripe.overrideApiBase(String.format("http://localhost:%d", serverSocket.getLocalPort())); - final RequestOptions options = RequestOptions.builder().setReadTimeout(1).build(); + final RequestOptions options = + RequestOptions.builder().setReadTimeout(1).setMaxNetworkRetries(0).build(); Throwable exception = assertThrows( diff --git a/src/test/java/com/stripe/functional/v2/StripeCollectionTest.java b/src/test/java/com/stripe/functional/v2/StripeCollectionTest.java new file mode 100644 index 00000000000..4285aac2f7e --- /dev/null +++ b/src/test/java/com/stripe/functional/v2/StripeCollectionTest.java @@ -0,0 +1,233 @@ +package com.stripe.functional.v2; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +import com.google.common.base.Splitter; +import com.google.gson.reflect.TypeToken; +import com.stripe.BaseStripeTest; +import com.stripe.exception.StripeException; +import com.stripe.model.v2.StripeCollection; +import com.stripe.net.ApiRequest; +import com.stripe.net.ApiResource; +import com.stripe.net.ApiResource.RequestMethod; +import com.stripe.net.ApiService; +import com.stripe.net.BaseAddress; +import com.stripe.net.HttpHeaders; +import com.stripe.net.RequestOptions; +import com.stripe.net.StripeRequest; +import com.stripe.net.StripeResponse; +import com.stripe.net.StripeResponseGetter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +public class StripeCollectionTest extends BaseStripeTest { + private static class PageableModel extends ApiResource { + String id; + + public String getId() { + return id; + } + } + + private static class PageableService extends ApiService { + public PageableService(StripeResponseGetter g) { + super(g); + } + + public StripeCollection list(Map params, RequestOptions options) + throws StripeException { + return this.getResponseGetter() + .request( + new ApiRequest( + BaseAddress.API, RequestMethod.GET, "/v2/pageable_models", params, options), + new TypeToken>() {}.getType()); + } + } + + List calculateJsonPages(String... pages) { + final List jsonPages = new ArrayList<>(); + int i = 0; + Integer nextPage = 0; + for (final String page : pages) { + nextPage = i++; + if (i == pages.length) { + nextPage = null; + } + List objs = new ArrayList<>(); + if (!page.equals("")) { + for (final String id : Splitter.on(',').split(page)) { + objs.add(String.format("{\"id\": \"%s\"}", id)); + } + } + + if (nextPage == null) { + jsonPages.add( + String.format("{\"data\": [%s], \"next_page_url\": null}", String.join(",", objs))); + } else { + jsonPages.add( + String.format( + "{\"data\": [%s], \"next_page_url\": \"/v2/pageable_models/page_%d\"}", + String.join(",", objs), i)); + } + } + return jsonPages; + } + + /** Sets the mock page fixtures. */ + public void setUpMockPages(String... pages) throws IOException, StripeException { + final List jsonPages = calculateJsonPages(pages); + Mockito.doAnswer( + new Answer() { + private int count = 0; + + @Override + public StripeResponse answer(InvocationOnMock invocation) { + if (count >= pages.length) { + throw new RuntimeException("Page out of bounds"); + } + + return new StripeResponse( + 200, HttpHeaders.of(Collections.emptyMap()), jsonPages.get(count++)); + } + }) + .when(httpClientSpy) + .request(Mockito.any()); + } + + @Test + public void testAutoPagingIterableEmpty() throws Exception { + setUpMockPages(""); + + final Map params = new HashMap<>(); + final RequestOptions requestOptions = RequestOptions.builder().build(); + + final StripeCollection collection = + new PageableService(BaseStripeTest.networkSpy).list(params, requestOptions); + final List models = new ArrayList<>(); + System.out.println(collection); + + for (PageableModel model : collection.autoPagingIterable()) { + models.add(model); + } + + assertEquals(0, models.size()); + verifyRequest( + BaseAddress.API, ApiResource.RequestMethod.GET, "/v2/pageable_models", params, null); + verifyNoMoreInteractions(networkSpy); + } + + @Test + public void testAutoPagingIterableSinglePage() throws Exception { + setUpMockPages("1,2,3"); + + final Map params = new HashMap<>(); + final RequestOptions requestOptions = RequestOptions.builder().build(); + + final StripeCollection collection = + new PageableService(BaseStripeTest.networkSpy).list(params, requestOptions); + final List models = new ArrayList<>(); + + for (PageableModel model : collection.autoPagingIterable()) { + models.add(model); + } + + assertEquals(3, models.size()); + assertEquals("1", models.get(0).getId()); + assertEquals("2", models.get(1).getId()); + assertEquals("3", models.get(2).getId()); + verifyRequest( + BaseAddress.API, ApiResource.RequestMethod.GET, "/v2/pageable_models", params, null); + verifyNoMoreInteractions(networkSpy); + } + + @Test + public void testAutoPagingIterableMultiplePages() throws Exception { + setUpMockPages("1,2", "3,4", "5"); + // set some arbitrary parameters so that we can verify that they're + // used for requests on the first page only + final Map page0Params = new HashMap<>(); + page0Params.put("foo", "bar"); + + final Map nextPageParams = new HashMap<>(); + + final RequestOptions ro = RequestOptions.builder().build(); + + final StripeCollection collection = + new PageableService(BaseStripeTest.networkSpy).list(page0Params, ro); + + final List models = new ArrayList<>(); + for (PageableModel model : collection.autoPagingIterable()) { + models.add(model); + } + + assertEquals(5, models.size()); + assertEquals("1", models.get(0).getId()); + assertEquals("2", models.get(1).getId()); + assertEquals("3", models.get(2).getId()); + assertEquals("4", models.get(3).getId()); + assertEquals("5", models.get(4).getId()); + + verifyRequest( + BaseAddress.API, ApiResource.RequestMethod.GET, "/v2/pageable_models", page0Params, null); + verifyRequest( + BaseAddress.API, + ApiResource.RequestMethod.GET, + "/v2/pageable_models/page_1", + nextPageParams, + null); + verifyRequest( + BaseAddress.API, + ApiResource.RequestMethod.GET, + "/v2/pageable_models/page_2", + nextPageParams, + null); + verifyNoMoreInteractions(networkSpy); + } + + @Test + public void testAutoPagingIterableWithRequestOptions() throws Exception { + setUpMockPages("1,2", "3,4", "5"); + + final RequestOptions requestOptions = + RequestOptions.builder().setApiKey("custom_api_key").build(); + + final StripeCollection collection = + new PageableService(BaseStripeTest.networkSpy).list(null, null); + + final List models = new ArrayList<>(); + for (PageableModel model : collection.autoPagingIterable(requestOptions)) { + models.add(model); + } + + // no params needed or wanted with the nextPageUrl + final Map nextPageParams = new HashMap<>(); + + assertEquals(5, models.size()); + + verifyRequest( + BaseAddress.API, ApiResource.RequestMethod.GET, "/v2/pageable_models", null, null); + verifyRequest( + BaseAddress.API, + ApiResource.RequestMethod.GET, + "/v2/pageable_models/page_1", + nextPageParams, + requestOptions); + verifyRequest( + BaseAddress.API, + ApiResource.RequestMethod.GET, + "/v2/pageable_models/page_2", + nextPageParams, + requestOptions); + + verifyNoMoreInteractions(networkSpy); + } +} diff --git a/src/test/java/com/stripe/model/InstantDeserializerTest.java b/src/test/java/com/stripe/model/InstantDeserializerTest.java new file mode 100644 index 00000000000..db179fafab1 --- /dev/null +++ b/src/test/java/com/stripe/model/InstantDeserializerTest.java @@ -0,0 +1,34 @@ +package com.stripe.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.stripe.BaseStripeTest; +import com.stripe.net.ApiResource; +import java.time.Instant; +import org.junit.jupiter.api.Test; + +public class InstantDeserializerTest extends BaseStripeTest { + + private static Gson gson = ApiResource.GSON; + + @Test + public void deserializeNull() { + final String json = gson.toJson(null); + // Gson also uses TypeTokens internally to get around Type Erasure for generic types, simulate + // that here: + final Instant out = gson.fromJson(json, new TypeToken() {}.getType()); + assertNull(out); + } + + @Test + public void deserializeString() { + final String json = gson.toJson("2022-02-15T00:27:45.330Z"); + // Gson also uses TypeTokens internally to get around Type Erasure for generic types, simulate + // that here: + final Instant out = gson.fromJson(json, new TypeToken() {}.getType()); + assertEquals(Instant.parse("2022-02-15T00:27:45.330Z"), out); + } +} diff --git a/src/test/java/com/stripe/model/InstantSerializerTest.java b/src/test/java/com/stripe/model/InstantSerializerTest.java new file mode 100644 index 00000000000..77548b2c886 --- /dev/null +++ b/src/test/java/com/stripe/model/InstantSerializerTest.java @@ -0,0 +1,53 @@ +package com.stripe.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.stripe.BaseStripeTest; +import com.stripe.net.ApiResource; +import java.time.Instant; +import org.junit.jupiter.api.Test; + +public class InstantSerializerTest extends BaseStripeTest { + + private static Gson gson = ApiResource.GSON; + + private static class TestTopLevelObject extends StripeObject { + @SuppressWarnings("unused") + Instant nested; + } + + @Test + public void serializeInstant() { + final TestTopLevelObject object = new TestTopLevelObject(); + object.nested = Instant.parse("2022-02-15T00:27:45.330Z"); + + final String expected = "{\n \"nested\": \"2022-02-15T00:27:45.330Z\"\n}"; + assertEquals(expected, object.toJson()); + } + + @Test + public void serializeNull() { + final TestTopLevelObject object = new TestTopLevelObject(); + object.nested = null; + + final String expected = "{\n \"nested\": null\n}"; + assertEquals(expected, object.toJson()); + } + + @Test + public void serializeDeserialize() { + final TestTopLevelObject object = new TestTopLevelObject(); + final Instant instant = Instant.parse("2022-02-15T00:27:45.330Z"); + object.nested = Instant.parse("2022-02-15T00:27:45.330Z"); + + final String json = object.toJson(); + + // Gson also uses TypeTokens internally to get around Type Erasure for generic types, simulate + // that here: + final TestTopLevelObject out = + gson.fromJson(json, new TypeToken() {}.getType()); + assertEquals(instant, out.nested); + } +} diff --git a/src/test/java/com/stripe/model/PagingIteratorTest.java b/src/test/java/com/stripe/model/PagingIteratorTest.java index 8935b3f1cf6..ad09d0d914f 100644 --- a/src/test/java/com/stripe/model/PagingIteratorTest.java +++ b/src/test/java/com/stripe/model/PagingIteratorTest.java @@ -136,6 +136,7 @@ void testAutoPaginationFromReferencedCollection() throws StripeException, IOExce "{\"id\": \"xyz\", \"pages\": {\"data\": [{\"id\": \"pm_121\"}, {\"id\": \"pm_122\"}], \"url\": \"/v1/pageable_models\", \"has_more\": true}}")) .when(httpClientSpy) .request(Mockito.any()); + Stripe.apiKey = null; ReferencesPageableModel model = ReferencesPageableModel.retrieve(RequestOptions.builder().setApiKey("sk_test_xyz").build()); diff --git a/src/test/java/com/stripe/model/SearchPagingIteratorTest.java b/src/test/java/com/stripe/model/SearchPagingIteratorTest.java index 38783e74bfb..f0a6afb5177 100644 --- a/src/test/java/com/stripe/model/SearchPagingIteratorTest.java +++ b/src/test/java/com/stripe/model/SearchPagingIteratorTest.java @@ -113,6 +113,7 @@ public void testAutoPagination() throws StripeException { assertEquals("pm_126", models.get(3).getId()); assertEquals("pm_127", models.get(4).getId()); + // First request made using a static method verifyRequest( BaseAddress.API, ApiResource.RequestMethod.GET, "/v1/searchable_models", page0Params, null); verifyRequest( diff --git a/src/test/java/com/stripe/model/StandardizationTest.java b/src/test/java/com/stripe/model/StandardizationTest.java new file mode 100644 index 00000000000..6e02f98ae4d --- /dev/null +++ b/src/test/java/com/stripe/model/StandardizationTest.java @@ -0,0 +1,156 @@ +package com.stripe.model; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.reflect.ClassPath; +import com.google.common.reflect.Invokable; +import com.google.common.reflect.Parameter; +import com.stripe.net.ApiRequestParams; +import com.stripe.net.ApiResource; +import com.stripe.net.RequestOptions; +import java.io.IOException; +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; + +/** Simple test to make sure stripe-java provides consistent bindings. */ +public class StandardizationTest { + @Test + public void allNonDeprecatedMethodsTakeOptions() throws IOException, NoSuchMethodException { + for (Class model : getAllModels()) { + for (Method method : model.getMethods()) { + // Skip methods not declared on the base class. + if (method.getDeclaringClass() != model) { + continue; + } + // Skip equals + if (method.getName().equals("equals")) { + continue; + } + // Skip hashCode + if (method.getName().equals("hashCode")) { + continue; + } + // Skip setters + if (method.getName().startsWith("set")) { + continue; + } + // Skip getters + if (method.getName().startsWith("get")) { + continue; + } + // Skip internal methods + if (method.getName().startsWith("_")) { + continue; + } + + Class[] parameterTypes = method.getParameterTypes(); + + // If more than one method with the same parameter types is declared in a class, and one of + // these methods has a return type that is more specific than any of the others, that method + // is returned; otherwise one of the methods is chosen arbitrarily. + Method mostSpecificMethod = model.getDeclaredMethod(method.getName(), parameterTypes); + if (!method.equals(mostSpecificMethod)) { + continue; + } + + Invokable invokable = Invokable.from(method); + // Skip private methods. + if (invokable.isPrivate()) { + continue; + } + // Skip deprecated methods - we need to keep them around, but aren't asserting their type. + if (invokable.isAnnotationPresent(Deprecated.class)) { + continue; + } + ImmutableList parameters = invokable.getParameters(); + // Skip empty parameter lists - assume the author is using default values for the + // RequestOptions + if (parameters.isEmpty()) { + continue; + } + Parameter lastParam = parameters.get(parameters.size() - 1); + Class finalParamType = lastParam.getType().getRawType(); + + // Skip methods that have exactly one param which is a map. + boolean isRequestParamType = + ApiRequestParams.class.isAssignableFrom(finalParamType) + || Map.class.equals(finalParamType); + if (isRequestParamType && parameters.size() == 1) { + continue; + } + + // Skip `public static Foo retrieve(String id) {...` helper methods + if (String.class.equals(finalParamType) + && parameters.size() == 1 + && method.getName().startsWith("retrieve")) { + continue; + } + + // Skip the `public static Card createCard(String id) {...` helper method on Customer. + if (String.class.equals(finalParamType) + && parameters.size() == 1 + && ("createCard".equals(method.getName()) + || "createBankAccount".equals(method.getName()))) { + continue; + } + + if (RequestOptions.class.isAssignableFrom(finalParamType)) { + continue; + } + + // Check if an overload with RequestOptions as the last parameter exists + if (!Arrays.stream(parameterTypes).anyMatch(p -> p.equals(RequestOptions.class))) { + Class[] overloadParameterTypes = + Arrays.copyOf(parameterTypes, parameterTypes.length + 1); + Array.set( + overloadParameterTypes, overloadParameterTypes.length - 1, RequestOptions.class); + Method overloadedMethod = + model.getDeclaredMethod(method.getName(), overloadParameterTypes); + if (overloadedMethod != null) { + continue; + } + } + + assertTrue( + RequestOptions.class.isAssignableFrom(finalParamType), + String.format( + "Methods on %ss like %s.%s should take a final " + + "parameter as a %s parameter, but got %s.%n", + ApiResource.class.getSimpleName(), + model.getSimpleName(), + method.getName(), + RequestOptions.class.getSimpleName(), + finalParamType.getCanonicalName())); + } + } + } + + private Collection> getAllModels() throws IOException { + Class chargeClass = Charge.class; + ClassPath classPath = ClassPath.from(chargeClass.getClassLoader()); + ImmutableSet topLevelClasses = + classPath.getTopLevelClasses(chargeClass.getPackage().getName()); + List> classList = Lists.newArrayListWithExpectedSize(topLevelClasses.size()); + for (ClassPath.ClassInfo classInfo : topLevelClasses) { + Class c = classInfo.load(); + // Skip things that aren't APIResources + if (!ApiResource.class.isAssignableFrom(c)) { + continue; + } + // Skip the APIResource itself + if (ApiResource.class == c) { + continue; + } + classList.add(classInfo.load()); + } + return classList; + } +} diff --git a/src/test/java/com/stripe/model/v2/EventTests.java b/src/test/java/com/stripe/model/v2/EventTests.java new file mode 100644 index 00000000000..8e06a7405e3 --- /dev/null +++ b/src/test/java/com/stripe/model/v2/EventTests.java @@ -0,0 +1,162 @@ +package com.stripe.model.v2; + +import static org.junit.jupiter.api.Assertions.*; + +import com.stripe.BaseStripeTest; +import com.stripe.events.V1BillingMeterErrorReportTriggeredEvent; +import com.stripe.exception.StripeException; +import com.stripe.model.billing.Meter; +import com.stripe.net.ApiResource; +import java.io.IOException; +import java.time.Instant; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class EventTests extends BaseStripeTest { + public static String v2PayloadNoData = null; + public static String v2PayloadWithData = null; + public static String v2UnknownEventPayload = null; + + @BeforeEach + public void setUpFixtures() { + v2PayloadNoData = + "{\n" + + " \"id\": \"evt_234\",\n" + + " \"object\": \"event\",\n" + + " \"type\": \"v1.billing.meter.error_report_triggered\",\n" + + " \"created\": \"2022-02-15T00:27:45.330Z\",\n" + + " \"related_object\": {\n" + + " \"id\": \"meter_123\",\n" + + " \"type\": \"billing.meter\",\n" + + " \"url\": \"/v1/billing/meters/meter_123\",\n" + + " \"stripe_context\": \"acct_123\"\n" + + " }\n" + + "}"; + v2PayloadWithData = + "{\n" + + " \"id\": \"evt_234\",\n" + + " \"object\": \"event\",\n" + + " \"type\": \"v1.billing.meter.error_report_triggered\",\n" + + " \"created\": \"2022-02-15T00:27:45.330Z\",\n" + + " \"related_object\": {\n" + + " \"id\": \"meter_123\",\n" + + " \"type\": \"billing.meter\",\n" + + " \"url\": \"/v1/billing/meters/meter_123\",\n" + + " \"stripe_context\": \"acct_123\"\n" + + " },\n" + + " \"data\": {\n" + + " \"developer_message_summary\": \"foo\"\n" + + " }\n" + + "}"; + + // note this is purposefully not correct; don't try and correct it! + v2UnknownEventPayload = + "{\n" + + " \"id\": \"evt_234\",\n" + + " \"object\": \"event\",\n" + + " \"type\": \"financial_account.features_updated\",\n" + + " \"created\": \"2022-02-15T00:27:45.330Z\",\n" + + " \"related_object\": {\n" + + " \"id\": \"meter_123\",\n" + + " \"type\": \"financial_account\",\n" + + " \"url\": \"/v2/financial_accounts/meter_123\",\n" + + " \"stripe_context\": \"acct_123\"\n" + + " },\n" + + " \"data\": {\n" + + " \"v1_event_id\": \"evt_789\",\n" + + " \"enabled_features\": [\"foo\"]\n" + + " }\n" + + "}"; + } + + @Test + public void parsesUnknownV2Event() { + Event event = Event.parse(v2UnknownEventPayload); + assertEquals("evt_234", event.getId()); + assertEquals("financial_account.features_updated", event.getType()); + assertEquals(Instant.parse("2022-02-15T00:27:45.330Z"), event.getCreated()); + + // no data or related object on base Event; nothing to check here. + } + + @Test + public void parsesV2Event() { + V1BillingMeterErrorReportTriggeredEvent event = + (V1BillingMeterErrorReportTriggeredEvent) Event.parse(v2PayloadNoData); + assertEquals("evt_234", event.getId()); + assertEquals("v1.billing.meter.error_report_triggered", event.getType()); + assertEquals(Instant.parse("2022-02-15T00:27:45.330Z"), event.getCreated()); + + assertEquals("meter_123", event.getRelatedObject().getId()); + assertEquals("billing.meter", event.getRelatedObject().getType()); + assertEquals("/v1/billing/meters/meter_123", event.getRelatedObject().getUrl()); + } + + @Test + public void parsesV2EventAndDeserializesEventData() throws StripeException { + V1BillingMeterErrorReportTriggeredEvent event = + (V1BillingMeterErrorReportTriggeredEvent) Event.parse(v2PayloadWithData); + event.setResponseGetter(networkSpy); + stubRequest( + ApiResource.RequestMethod.GET, + String.format("/v2/core/events/%s", event.getId()), + null, + Event.class, + v2PayloadWithData); + + V1BillingMeterErrorReportTriggeredEvent.EventData data = event.getData(); + + assertEquals("foo", data.getDeveloperMessageSummary()); + } + + @Test + public void retrieveObjectFetchesAndDeserializesObject() throws StripeException, IOException { + V1BillingMeterErrorReportTriggeredEvent event = + (V1BillingMeterErrorReportTriggeredEvent) Event.parse(v2PayloadNoData); + event.setResponseGetter(networkSpy); + stubRequest( + ApiResource.RequestMethod.GET, + "/v1/billing/meters/meter_123", + null, + Meter.class, + getResourceAsString("/api_fixtures/billing_meter.json")); + + assertEquals("/v1/billing/meters/meter_123", event.getRelatedObject().url); + assertEquals("meter_123", event.getRelatedObject().id); + assertEquals("billing.meter", event.getRelatedObject().type); + + Meter meter = event.fetchRelatedObject(); + + assertEquals("meter_123", meter.getId()); + assertEquals("billing.meter", meter.getObject()); + assertEquals(1727303036, meter.getCreated()); + assertEquals("e1", meter.getCustomerMapping().getEventPayloadKey()); + assertEquals("by_id", meter.getCustomerMapping().getType()); + assertEquals("sum", meter.getDefaultAggregation().getFormula()); + assertEquals("API Requests", meter.getDisplayName()); + assertEquals("API Request Made", meter.getEventName()); + assertEquals("day", meter.getEventTimeWindow()); + assertFalse(meter.getLivemode()); + assertEquals("active", meter.getStatus()); + assertNull(meter.getStatusTransitions().getDeactivatedAt()); + assertEquals(1727303036, meter.getUpdated()); + } + + // FIXME (jar) this should no longer be possible; confirm this and remove before merge + // @Test + // public void retrieveObjectFetchesAndDeserializesUnknownObject() + // throws StripeException, IOException { + // FinancialAccountBalanceOpenedEvent event = + // (FinancialAccountBalanceOpenedEvent) + // Event.parse(v2PayloadNoData.replace("\"type\": \"card\"", "\"type\": \"cardio\"")); + // event.setResponseGetter(networkSpy); + // stubRequest( + // ApiResource.RequestMethod.GET, + // "/v2/financial_accounts/meter_123", + // null, + // StripeRawJsonObject.class, + // getResourceAsString("/api_fixtures/card.json")); + + // assertInstanceOf(StripeRawJsonObject.class, event.fetchObject()); + // } +} diff --git a/src/test/java/com/stripe/net/ApiRequestParamsConverterTest.java b/src/test/java/com/stripe/net/ApiRequestParamsConverterTest.java index 22805238097..88a9f532212 100644 --- a/src/test/java/com/stripe/net/ApiRequestParamsConverterTest.java +++ b/src/test/java/com/stripe/net/ApiRequestParamsConverterTest.java @@ -7,10 +7,12 @@ import com.google.gson.annotations.SerializedName; import com.stripe.param.common.EmptyParam; +import java.time.Instant; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import junit.framework.TestCase; import org.junit.jupiter.api.Test; public class ApiRequestParamsConverterTest { @@ -115,6 +117,11 @@ private static class IllegalModelHasWrongExtraParamsType extends ApiRequestParam String extraParams; } + private static class HasInstantParam extends ApiRequestParams { + @SerializedName("instant_param") + public Instant instantParam; + } + @Test public void testHasExtraParams() { ModelHasExtraParams params = new ModelHasExtraParams(ParamCode.ENUM_FOO); @@ -281,6 +288,18 @@ public void testObjectMaps() { assertEquals(objBar.get("hello"), "world"); } + @Test + public void testToMapWithInstantParams() { + HasInstantParam params = new HasInstantParam(); + params.instantParam = Instant.ofEpochSecond(987654321); + Map paramMap = toMap(params); + TestCase.assertEquals(1, paramMap.size()); + + // primitive boolean is default false and is converted into map param accordingly + TestCase.assertTrue(paramMap.containsKey("instant_param")); + TestCase.assertEquals("2001-04-19T04:25:21Z", paramMap.get("instant_param")); + } + private Map toMap(ApiRequestParams params) { return converter.convert(params); } diff --git a/src/test/java/com/stripe/net/ApiRequestParamsTest.java b/src/test/java/com/stripe/net/ApiRequestParamsTest.java index fb903d2fd76..ecdd2ad967f 100644 --- a/src/test/java/com/stripe/net/ApiRequestParamsTest.java +++ b/src/test/java/com/stripe/net/ApiRequestParamsTest.java @@ -9,7 +9,7 @@ import com.stripe.param.common.EmptyParam; import java.util.Map; import lombok.Setter; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class ApiRequestParamsTest { enum ParamCode implements ApiRequestParams.EnumParam { diff --git a/src/test/java/com/stripe/net/ClientOptionsTest.java b/src/test/java/com/stripe/net/ClientOptionsTest.java index 673ed9045b9..139b8d39974 100644 --- a/src/test/java/com/stripe/net/ClientOptionsTest.java +++ b/src/test/java/com/stripe/net/ClientOptionsTest.java @@ -21,6 +21,7 @@ public void GlobalClientOptionsReflectsGlobalConfiguration() { String origApiBase = Stripe.getApiBase(); String origUploadBase = Stripe.getUploadBase(); String origConnectBase = Stripe.getConnectBase(); + String origMeterEventsBase = Stripe.getMeterEventsBase(); GlobalStripeResponseGetterOptions global = GlobalStripeResponseGetterOptions.INSTANCE; @@ -39,6 +40,7 @@ public void GlobalClientOptionsReflectsGlobalConfiguration() { Stripe.overrideApiBase("http://api.base"); Stripe.overrideConnectBase("http://connect.base"); Stripe.overrideUploadBase("http://upload.base"); + Stripe.overrideMeterEventsBase("http://meter-events.base"); assertEquals(1, global.getConnectTimeout()); assertEquals(1, global.getMaxNetworkRetries()); @@ -49,6 +51,7 @@ public void GlobalClientOptionsReflectsGlobalConfiguration() { assertEquals("http://api.base", global.getApiBase()); assertEquals("http://connect.base", global.getConnectBase()); assertEquals("http://upload.base", global.getFilesBase()); + assertEquals("http://meter-events.base", global.getMeterEventsBase()); } finally { Stripe.apiKey = origApiKey; Stripe.setConnectTimeout(origConnectTimeout); @@ -60,6 +63,7 @@ public void GlobalClientOptionsReflectsGlobalConfiguration() { Stripe.overrideApiBase(origApiBase); Stripe.overrideConnectBase(origConnectBase); Stripe.overrideUploadBase(origUploadBase); + Stripe.overrideMeterEventsBase(origMeterEventsBase); } } } diff --git a/src/test/java/com/stripe/net/FormEncoderTest.java b/src/test/java/com/stripe/net/FormEncoderTest.java index bad157d52e1..e3e0289628c 100644 --- a/src/test/java/com/stripe/net/FormEncoderTest.java +++ b/src/test/java/com/stripe/net/FormEncoderTest.java @@ -26,7 +26,10 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; +import javax.annotation.Nullable; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.RequiredArgsConstructor; import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; @@ -127,9 +130,12 @@ public void testCreateQueryString() { org.junit.Assume.assumeTrue(!System.getProperty("java.version").startsWith("10.")); @Data + @RequiredArgsConstructor + @AllArgsConstructor class TestCase { private final Map data; private final String want; + @Nullable private Boolean arrayAsRepeated = false; } List testCases = @@ -370,6 +376,18 @@ class TestCase { "array", new Object[] {new String[] {"foo", "bar"}, new int[] {1, 2, 3}}), "array[0][0]=foo&array[0][1]=bar&array[1][0]=1&array[1][1]=2&array[1][2]=3")); + // Array (arrayAsRepeated) + add(new TestCase(Collections.singletonMap("array", new String[] {}), "", true)); + add( + new TestCase( + Collections.singletonMap("array", new String[] {"1", "2", "3"}), + "array=1&array=2&array=3", + true)); + add( + new TestCase( + Collections.singletonMap("array", new Object[] {123, "foo"}), + "array=123&array=foo", + true)); // Collection add( new TestCase( @@ -411,7 +429,9 @@ class TestCase { }; for (TestCase testCase : testCases) { - assertEquals(testCase.getWant(), FormEncoder.createQueryString(testCase.getData())); + assertEquals( + testCase.getWant(), + FormEncoder.createQueryString(testCase.getData(), testCase.getArrayAsRepeated())); } } @@ -461,7 +481,7 @@ class TestCase { }; for (TestCase testCase : testCases) { - assertEquals(testCase.getWant(), FormEncoder.flattenParams(testCase.getData())); + assertEquals(testCase.getWant(), FormEncoder.flattenParams(testCase.getData(), false)); } } diff --git a/src/test/java/com/stripe/net/HttpClientTest.java b/src/test/java/com/stripe/net/HttpClientTest.java index 144bd9b0a54..be97783af65 100644 --- a/src/test/java/com/stripe/net/HttpClientTest.java +++ b/src/test/java/com/stripe/net/HttpClientTest.java @@ -34,11 +34,12 @@ public void setUpFixtures() throws StripeException { this.client.networkRetriesSleep = false; this.request = - new StripeRequest( + StripeRequest.create( ApiResource.RequestMethod.GET, "http://example.com/get", null, - RequestOptions.builder().setApiKey("sk_test_123").setMaxNetworkRetries(2).build()); + RequestOptions.builder().setApiKey("sk_test_123").setMaxNetworkRetries(2).build(), + ApiMode.V1); } @Test diff --git a/src/test/java/com/stripe/net/HttpContentTest.java b/src/test/java/com/stripe/net/HttpContentTest.java index dd9ee043d65..3bbada57aaf 100644 --- a/src/test/java/com/stripe/net/HttpContentTest.java +++ b/src/test/java/com/stripe/net/HttpContentTest.java @@ -8,16 +8,24 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import org.junit.jupiter.api.Test; public class HttpContentTest extends BaseStripeTest { @Test public void testBuildFormURLEncodedContentNull() throws IOException { + String stringContent = null; + Collection> nameValueCollectionContent = null; assertThrows( NullPointerException.class, () -> { - HttpContent.buildFormURLEncodedContent(null); + HttpContent.buildFormURLEncodedContent(stringContent); + }); + assertThrows( + NullPointerException.class, + () -> { + HttpContent.buildFormURLEncodedContent(nameValueCollectionContent); }); } diff --git a/src/test/java/com/stripe/net/JsonEncoderTest.java b/src/test/java/com/stripe/net/JsonEncoderTest.java new file mode 100644 index 00000000000..79cc8879762 --- /dev/null +++ b/src/test/java/com/stripe/net/JsonEncoderTest.java @@ -0,0 +1,67 @@ +package com.stripe.net; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.google.gson.annotations.SerializedName; +import com.stripe.BaseStripeTest; +import com.stripe.param.common.EmptyParam; +import java.io.IOException; +import org.junit.jupiter.api.Test; + +public class JsonEncoderTest extends BaseStripeTest { + enum TestEnum { + @SerializedName("foo") + FOO, + + @SerializedName("bar") + BAR, + } + + static class TestParams extends ApiRequestParams { + @SerializedName("name") + public Object name; + + @SerializedName("nested") + public NestedParams nested; + + @SerializedName("enum") + public TestEnum eenum; + } + + static class NestedParams { + @SerializedName("email") + public Object email; + } + + @Test + public void testCreateJsonContent() throws IOException { + TestParams params = new TestParams(); + params.name = "name"; + params.nested = new NestedParams(); + params.nested.email = "a@example.com"; + params.eenum = TestEnum.FOO; + + HttpContent content = JsonEncoder.createHttpContent(ApiRequestParams.paramsToMap(params)); + + assertNotNull(content); + assertEquals("application/json", content.contentType()); + assertEquals( + "{\"name\":\"name\",\"nested\":{\"email\":\"a@example.com\"},\"enum\":\"foo\"}", + content.stringContent()); + } + + @Test + public void testCreateJsonContentEmptyParam() throws IOException { + TestParams params = new TestParams(); + params.name = EmptyParam.EMPTY; + params.nested = new NestedParams(); + params.nested.email = EmptyParam.EMPTY; + + HttpContent content = JsonEncoder.createHttpContent(ApiRequestParams.paramsToMap(params)); + + assertNotNull(content); + assertEquals("application/json", content.contentType()); + assertEquals("{\"name\":null,\"nested\":{\"email\":null}}", content.stringContent()); + } +} diff --git a/src/test/java/com/stripe/net/RequestOptionsTest.java b/src/test/java/com/stripe/net/RequestOptionsTest.java index 1e4a1a852b6..156ee92a1e3 100644 --- a/src/test/java/com/stripe/net/RequestOptionsTest.java +++ b/src/test/java/com/stripe/net/RequestOptionsTest.java @@ -132,13 +132,14 @@ public void mergeOverwritesClientOptions() { StripeResponseGetterOptions clientOptions = TestStripeResponseGetterOptions.builder() - .setApiKey("key1") + .setAuthenticator(new BearerTokenAuthenticator("key1")) .setConnectTimeout(1) .setMaxNetworkRetries(2) .setReadTimeout(3) .setClientId("1") .setConnectionProxy(clientProxy) .setProxyCredential(clientProxyCred) + .setStripeContext("globalContext") .build(); RequestOptions requestOptions = @@ -152,6 +153,7 @@ public void mergeOverwritesClientOptions() { .setProxyCredential(requestProxyCred) .setIdempotencyKey("3") .setStripeAccount("4") + .setStripeContext("5") .build(); RequestOptions merged = RequestOptions.merge(clientOptions, requestOptions); @@ -164,6 +166,7 @@ public void mergeOverwritesClientOptions() { assertEquals(requestProxyCred, merged.getProxyCredential()); assertEquals("3", merged.getIdempotencyKey()); assertEquals("4", merged.getStripeAccount()); + assertEquals("5", merged.getStripeContext()); } @Test @@ -174,13 +177,14 @@ public void mergeFallsBackToClientOptions() { StripeResponseGetterOptions clientOptions = TestStripeResponseGetterOptions.builder() - .setApiKey("key1") + .setAuthenticator(new BearerTokenAuthenticator("key1")) .setConnectTimeout(1) .setMaxNetworkRetries(1) .setReadTimeout(1) .setClientId("1") .setConnectionProxy(clientProxy) .setProxyCredential(clientProxyCred) + .setStripeContext("global context") .build(); RequestOptions requestOptions = RequestOptions.builder().build(); @@ -195,6 +199,7 @@ public void mergeFallsBackToClientOptions() { assertEquals(clientProxyCred, merged.getProxyCredential()); assertEquals(null, merged.getIdempotencyKey()); assertEquals(null, merged.getStripeAccount()); + assertEquals("global context", merged.getStripeContext()); } @Test diff --git a/src/test/java/com/stripe/net/RequestSigningAuthenticatorTest.java b/src/test/java/com/stripe/net/RequestSigningAuthenticatorTest.java new file mode 100644 index 00000000000..992cdb8b354 --- /dev/null +++ b/src/test/java/com/stripe/net/RequestSigningAuthenticatorTest.java @@ -0,0 +1,128 @@ +package com.stripe.net; + +import static org.junit.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.common.collect.ImmutableMap; +import com.stripe.BaseStripeTest; +import com.stripe.exception.StripeException; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import org.junit.jupiter.api.Test; + +public class RequestSigningAuthenticatorTest extends BaseStripeTest { + Function testCurrentTimeGetter = + (l) -> { + return new RequestSigningAuthenticator.CurrentTimeInSecondsGetter() { + @Override + public Long getCurrentTimeInSeconds() { + return l; + } + }; + }; + + @Test + public void appliesSignatureToRequest() throws StripeException { + List signatureBases = new ArrayList<>(); + + StripeRequest request = + StripeRequest.create( + ApiResource.RequestMethod.POST, + "http://example.com/get", + ImmutableMap.of("string", "String!"), + RequestOptions.builder() + .setAuthenticator( + new RequestSigningAuthenticator("keyid") { + @Override + public byte[] sign(byte[] signatureBase) throws GeneralSecurityException { + signatureBases.add(signatureBase); + return new byte[] {1, 2, 3, 4, 5}; + } + }.withCurrentTimeInSecondsGetter(testCurrentTimeGetter.apply(123456789L))) + .build(), + ApiMode.V2); + + assertEquals( + "\"content-type\": application/json\n" + + "\"content-digest\": sha-256=:HA3i38j+04ac71IzPtG1JK8o4q9sPK0fYPmJHmci5bg=:\n" + + "\"stripe-context\": \n" + + "\"stripe-account\": \n" + + "\"authorization\": STRIPE-V2-SIG keyid\n" + + "\"@signature-params\": (\"content-type\" \"content-digest\" \"stripe-context\" \"stripe-account\" \"authorization\");created=123456789", + new String(signatureBases.get(0), StandardCharsets.UTF_8)); + assertEquals( + "sig1=(\"content-type\" \"content-digest\" \"stripe-context\" \"stripe-account\" \"authorization\");" + + "created=123456789", + request.headers().firstValue("Signature-Input").get()); + assertEquals("sig1=:AQIDBAU=:", request.headers().firstValue("Signature").get()); + assertEquals( + "sha-256=:HA3i38j+04ac71IzPtG1JK8o4q9sPK0fYPmJHmci5bg=:", + request.headers().firstValue("Content-Digest").get()); + assertEquals("STRIPE-V2-SIG keyid", request.headers().firstValue("Authorization").get()); + assertEquals("application/json", request.headers().firstValue("Content-Type").get()); + } + + @Test + public void appliesSignatureToGetRequest() throws StripeException { + List signatureBases = new ArrayList<>(); + + StripeRequest request = + StripeRequest.create( + ApiResource.RequestMethod.GET, + "http://example.com/get", + null, + RequestOptions.builder() + .setAuthenticator( + new RequestSigningAuthenticator("keyid") { + @Override + public byte[] sign(byte[] signatureBase) throws GeneralSecurityException { + signatureBases.add(signatureBase); + return new byte[] {1, 2, 3, 4, 5}; + } + }.withCurrentTimeInSecondsGetter(testCurrentTimeGetter.apply(123456789L))) + .build(), + ApiMode.V2); + + assertEquals( + "\"stripe-context\": \n" + + "\"stripe-account\": \n" + + "\"authorization\": STRIPE-V2-SIG keyid\n" + + "\"@signature-params\": (\"stripe-context\" \"stripe-account\" \"authorization\");created=123456789", + new String(signatureBases.get(0), StandardCharsets.UTF_8)); + assertEquals( + "sig1=(\"stripe-context\" \"stripe-account\" \"authorization\");" + "created=123456789", + request.headers().firstValue("Signature-Input").get()); + assertEquals("sig1=:AQIDBAU=:", request.headers().firstValue("Signature").get()); + assertEquals(false, request.headers().firstValue("Content-Digest").isPresent()); + assertEquals("STRIPE-V2-SIG keyid", request.headers().firstValue("Authorization").get()); + } + + @Test + public void wrapsSecurityException() { + StripeException exception = + assertThrows( + StripeException.class, + () -> + StripeRequest.create( + ApiResource.RequestMethod.POST, + "http://example.com/get", + ImmutableMap.of("string", "String!"), + RequestOptions.builder() + .setAuthenticator( + new RequestSigningAuthenticator("keyid") { + @Override + public byte[] sign(byte[] signatureBase) + throws GeneralSecurityException { + throw new GeneralSecurityException("something bad happened"); + } + }) + .build(), + ApiMode.V2)); + + assertEquals("Error calculating request signature.", exception.getMessage()); + assertEquals("something bad happened", exception.getCause().getMessage()); + } +} diff --git a/src/test/java/com/stripe/net/StripeRequestTest.java b/src/test/java/com/stripe/net/StripeRequestTest.java index 4b5c00b9b76..ace1f86f158 100644 --- a/src/test/java/com/stripe/net/StripeRequestTest.java +++ b/src/test/java/com/stripe/net/StripeRequestTest.java @@ -9,6 +9,7 @@ import com.stripe.exception.AuthenticationException; import com.stripe.exception.StripeException; import com.stripe.net.RequestOptions.RequestOptionsBuilder; +import com.stripe.param.common.EmptyParam; import java.nio.charset.StandardCharsets; import org.junit.jupiter.api.Test; @@ -35,11 +36,12 @@ static class NestedParams { @Test public void testCtorGetRequest() throws StripeException { StripeRequest request = - new StripeRequest( + StripeRequest.create( ApiResource.RequestMethod.GET, "http://example.com/get", ImmutableMap.of("string", "String!"), - options); + options, + ApiMode.V1); assertEquals(ApiResource.RequestMethod.GET, request.method()); assertEquals("http://example.com/get?string=String%21", request.url().toString()); @@ -54,11 +56,12 @@ public void testCtorGetRequest() throws StripeException { @Test public void testCtorGetRequestWithQueryString() throws StripeException { StripeRequest request = - new StripeRequest( + StripeRequest.create( ApiResource.RequestMethod.GET, "http://example.com/get?customer=cus_xxx", ImmutableMap.of("string", "String!"), - options); + options, + ApiMode.V1); assertEquals(ApiResource.RequestMethod.GET, request.method()); assertEquals( @@ -74,11 +77,12 @@ public void testCtorGetRequestWithQueryString() throws StripeException { @Test public void testCtorPostRequest() throws StripeException { StripeRequest request = - new StripeRequest( + StripeRequest.create( ApiResource.RequestMethod.POST, "http://example.com/post", ImmutableMap.of("string", "String!"), - options); + options, + ApiMode.V1); assertEquals(ApiResource.RequestMethod.POST, request.method()); assertEquals("http://example.com/post", request.url().toString()); @@ -96,11 +100,12 @@ public void testCtorPostRequest() throws StripeException { @Test public void testCtorDeleteRequest() throws StripeException { StripeRequest request = - new StripeRequest( + StripeRequest.create( ApiResource.RequestMethod.DELETE, "http://example.com/get", ImmutableMap.of("string", "String!"), - options); + options, + ApiMode.V1); assertEquals(ApiResource.RequestMethod.DELETE, request.method()); assertEquals("http://example.com/get?string=String%21", request.url().toString()); @@ -112,6 +117,47 @@ public void testCtorDeleteRequest() throws StripeException { assertNull(request.content()); } + @Test + public void testCtorV2PostRequest() throws StripeException { + StripeRequest request = + StripeRequest.create( + ApiResource.RequestMethod.POST, + "http://example.com/post", + ImmutableMap.of("string", "String!"), + options, + ApiMode.V2); + + assertEquals(ApiResource.RequestMethod.POST, request.method()); + assertEquals("http://example.com/post", request.url().toString()); + assertEquals("Bearer sk_test_123", request.headers().firstValue("Authorization").orElse(null)); + assertTrue(request.headers().firstValue("Stripe-Version").isPresent()); + assertTrue(request.headers().firstValue("Idempotency-Key").isPresent()); + assertFalse(request.headers().firstValue("Stripe-Account").isPresent()); + assertNotNull(request.content()); + assertEquals( + "{\"string\":\"String!\"}", + new String(request.content().byteArrayContent(), StandardCharsets.UTF_8)); + } + + @Test + public void testCtorV2DeleteRequest() throws StripeException { + StripeRequest request = + StripeRequest.create( + ApiResource.RequestMethod.DELETE, + "http://example.com/get", + ImmutableMap.of("string", "String!"), + options, + ApiMode.V2); + + assertEquals(ApiResource.RequestMethod.DELETE, request.method()); + assertEquals("http://example.com/get?string=String%21", request.url().toString()); + assertEquals("Bearer sk_test_123", request.headers().firstValue("Authorization").orElse(null)); + assertTrue(request.headers().firstValue("Stripe-Version").isPresent()); + assertTrue(request.headers().firstValue("Idempotency-Key").isPresent()); + assertFalse(request.headers().firstValue("Stripe-Account").isPresent()); + assertNull(request.content()); + } + @Test public void testCtorRequestOptions() throws StripeException { RequestOptions options = @@ -123,7 +169,8 @@ public void testCtorRequestOptions() throws StripeException { "2012-12-21") .build(); StripeRequest request = - new StripeRequest(ApiResource.RequestMethod.GET, "http://example.com/get", null, options); + StripeRequest.create( + ApiResource.RequestMethod.GET, "http://example.com/get", null, options, ApiMode.V1); assertEquals(ApiResource.RequestMethod.GET, request.method()); assertEquals("http://example.com/get", request.url().toString()); @@ -143,11 +190,12 @@ public void testCtorThrowsOnNullApiKey() throws StripeException { assertThrows( AuthenticationException.class, () -> { - new StripeRequest( + StripeRequest.create( ApiResource.RequestMethod.GET, "http://example.com/get", null, - RequestOptions.builder().build()); + RequestOptions.builder().setApiKey(null).build(), + ApiMode.V1); }); assertTrue(e.getMessage().contains("No API key provided.")); } @@ -158,46 +206,41 @@ public void testCtorThrowsOnEmptyApiKey() throws StripeException { assertThrows( AuthenticationException.class, () -> { - new StripeRequest( + StripeRequest.create( ApiResource.RequestMethod.GET, "http://example.com/get", null, - RequestOptions.builder().setApiKey("").build()); + RequestOptions.builder().setApiKey("").build(), + ApiMode.V1); }); assertTrue(e.getMessage().contains("Your API key is invalid, as it is an empty string.")); } @Test public void testCtorThrowsOnApiKeyContainingWhitespace() throws StripeException { - String origApiKey = Stripe.apiKey; - - try { - Stripe.apiKey = "sk_test_123\n"; - - AuthenticationException e = - assertThrows( - AuthenticationException.class, - () -> { - new StripeRequest( - ApiResource.RequestMethod.GET, - "http://example.com/get", - null, - RequestOptions.builder().setApiKey("sk_test _123\n").build()); - }); - assertTrue(e.getMessage().contains("Your API key is invalid, as it contains whitespace.")); - } finally { - Stripe.apiKey = origApiKey; - } + AuthenticationException e = + assertThrows( + AuthenticationException.class, + () -> { + StripeRequest.create( + ApiResource.RequestMethod.GET, + "http://example.com/get", + null, + RequestOptions.builder().setApiKey("sk_test _123\n").build(), + ApiMode.V1); + }); + assertTrue(e.getMessage().contains("Your API key is invalid, as it contains whitespace.")); } @Test public void testWithAdditionalHeader() throws StripeException { StripeRequest request = - new StripeRequest( + StripeRequest.create( ApiResource.RequestMethod.GET, "http://example.com/get", ImmutableMap.of("string", "String!"), - options); + options, + ApiMode.V1); StripeRequest updatedRequest = request.withAdditionalHeader("New-Header", "bar"); assertTrue(updatedRequest.headers().firstValue("New-Header").isPresent()); assertEquals("bar", updatedRequest.headers().firstValue("New-Header").get()); @@ -206,11 +249,13 @@ public void testWithAdditionalHeader() throws StripeException { @Test public void testBuildContentIsNullWhenRequestIsGet() throws StripeException { StripeRequest request = - new StripeRequest( + StripeRequest.create( ApiResource.RequestMethod.GET, "http://example.com/get", ImmutableMap.of("key", "value!"), - options); + options, + ApiMode.V1); + assertNull(request.content()); } @@ -218,15 +263,94 @@ public void testBuildContentIsNullWhenRequestIsGet() throws StripeException { public void testBuildContentHasFormEncodedContentWhenRequestIsPostAndApiVersionV1() throws StripeException { StripeRequest request = - new StripeRequest( + StripeRequest.create( ApiResource.RequestMethod.POST, "http://example.com/post", ImmutableMap.of("key", "value!"), - options); + options, + ApiMode.V1); + assertInstanceOf(HttpContent.class, request.content()); assertEquals( "application/x-www-form-urlencoded;charset=UTF-8", request.content().contentType()); assertArrayEquals( "key=value%21".getBytes(StandardCharsets.UTF_8), request.content().byteArrayContent()); } + + @Test + public void testBuildHeadersHasStripeContext() throws StripeException { + StripeRequest request = + StripeRequest.create( + ApiResource.RequestMethod.POST, + "http://example.com/post", + null, + RequestOptions.builder().setStripeContext("ctx").setApiKey("123").build(), + ApiMode.V2); + + assertEquals("ctx", request.headers().firstValue("Stripe-Context").get()); + } + + @Test + public void testBuildHeadersIgnoresNullAccount() throws StripeException { + StripeRequest request = + StripeRequest.create( + ApiResource.RequestMethod.POST, + "http://example.com/post", + null, + RequestOptions.builder().setStripeAccount(null).setApiKey("123").build(), + ApiMode.V2); + + assertFalse(request.headers().map().containsKey("Stripe-Account")); + } + + @Test + public void testBuildHeadersThrowsWhenContextPassedIntoV1Request() { + assertThrows( + UnsupportedOperationException.class, + () -> + StripeRequest.create( + ApiResource.RequestMethod.POST, + "http://example.com/post", + null, + RequestOptions.builder().setStripeContext("ctx").build(), + ApiMode.V1)); + } + + @Test + public void testBuildContentHasJsonContentWhenRequestIsPostAndApiVersionV2() + throws StripeException { + StripeRequest request = + StripeRequest.create( + ApiResource.RequestMethod.POST, + "http://example.com/post", + ImmutableMap.of("key", "value!"), + options, + ApiMode.V2); + + assertInstanceOf(HttpContent.class, request.content()); + assertEquals("application/json", request.content().contentType()); + assertEquals("application/json", request.headers().firstValue("Content-Type").get()); + + assertArrayEquals( + "{\"key\":\"value!\"}".getBytes(StandardCharsets.UTF_8), + request.content().byteArrayContent()); + } + + @Test + public void testBuildContentEncodesEmptyParamAsNullForV2JsonRequest() throws StripeException { + TestParams params = new TestParams(); + params.name = EmptyParam.EMPTY; + params.nested = new NestedParams(); + params.nested.email = EmptyParam.EMPTY; + + StripeRequest request = + StripeRequest.create( + ApiResource.RequestMethod.POST, + "http://example.com/post", + ApiRequestParams.paramsToMap(params), + options, + ApiMode.V2); + + assertEquals("{\"name\":null,\"nested\":{\"email\":null}}", request.content().stringContent()); + } } diff --git a/src/test/java/com/stripe/net/TestStripeResponseGetterOptions.java b/src/test/java/com/stripe/net/TestStripeResponseGetterOptions.java index 55d8a1334eb..9f35d5bb9f9 100644 --- a/src/test/java/com/stripe/net/TestStripeResponseGetterOptions.java +++ b/src/test/java/com/stripe/net/TestStripeResponseGetterOptions.java @@ -10,7 +10,7 @@ public class TestStripeResponseGetterOptions extends StripeResponseGetterOptions { // When adding setting here keep them in sync with settings in RequestOptions and // in the RequestOptions.merge method - private final String apiKey; + private final Authenticator authenticator; private final String clientId; private final int connectTimeout; private final int readTimeout; @@ -20,9 +20,11 @@ public class TestStripeResponseGetterOptions extends StripeResponseGetterOptions private final String apiBase; private final String filesBase; private final String connectBase; + private final String meterEventsBase; + private final String stripeContext; public TestStripeResponseGetterOptions( - String apiKey, + Authenticator authenticator, String clientId, int connectTimeout, int readTimeout, @@ -31,8 +33,10 @@ public TestStripeResponseGetterOptions( PasswordAuthentication proxyCredential, String apiBase, String filesBase, - String connectBase) { - this.apiKey = apiKey; + String connectBase, + String meterEventsBase, + String stripeContext) { + this.authenticator = authenticator; this.clientId = clientId; this.connectTimeout = connectTimeout; this.readTimeout = readTimeout; @@ -42,5 +46,7 @@ public TestStripeResponseGetterOptions( this.apiBase = apiBase; this.filesBase = filesBase; this.connectBase = connectBase; + this.meterEventsBase = meterEventsBase; + this.stripeContext = stripeContext; } } diff --git a/src/test/java/com/stripe/net/WebhookTest.java b/src/test/java/com/stripe/net/WebhookTest.java index 174ebd987fe..70200718b11 100644 --- a/src/test/java/com/stripe/net/WebhookTest.java +++ b/src/test/java/com/stripe/net/WebhookTest.java @@ -36,7 +36,7 @@ public void setUpFixtures() { payload = "{\n \"id\": \"evt_test_webhook\",\n \"object\": \"event\"\n}"; } - public String generateSigHeader() throws NoSuchAlgorithmException, InvalidKeyException { + public static String generateSigHeader() throws NoSuchAlgorithmException, InvalidKeyException { final Map options = new HashMap<>(); return generateSigHeader(options); } @@ -47,7 +47,7 @@ public String generateSigHeader() throws NoSuchAlgorithmException, InvalidKeyExc * @param options Options map to override default values * @return The contents of the generated header */ - public String generateSigHeader(Map options) + public static String generateSigHeader(Map options) throws NoSuchAlgorithmException, InvalidKeyException { final long timestamp = (options.get("timestamp") != null) @@ -285,7 +285,7 @@ public void testStripeClientConstructEvent() options.put("payload", payload); final String sigHeader = generateSigHeader(options); - final Event event = client.constructEvent(payload, sigHeader, secret); + final Event event = client.parseSnapshotEvent(payload, sigHeader, secret); final Reader reader = (Reader) event.getDataObjectDeserializer().getObject().get(); reader.delete(); @@ -318,7 +318,7 @@ public void testStripeClientConstructEventWithTolerance() options.put("payload", payload); final String sigHeader = generateSigHeader(options); - final Event event = client.constructEvent(payload, sigHeader, secret, 500); + final Event event = client.parseSnapshotEvent(payload, sigHeader, secret, 500); final Reader reader = (Reader) event.getDataObjectDeserializer().getObject().get(); reader.delete(); diff --git a/src/test/java/com/stripe/v2/AmountTest.java b/src/test/java/com/stripe/v2/AmountTest.java new file mode 100644 index 00000000000..5b532dc12d1 --- /dev/null +++ b/src/test/java/com/stripe/v2/AmountTest.java @@ -0,0 +1,35 @@ +package com.stripe.v2; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.stripe.BaseStripeTest; +import com.stripe.net.ApiResource; +import org.junit.jupiter.api.Test; + +public class AmountTest extends BaseStripeTest { + @Test + public void testDeserialize() throws Exception { + final Amount amount = + ApiResource.GSON.fromJson("{\"value\": 10, \"currency\": \"USD\"}", Amount.class); + assertNotNull(amount); + assertEquals(10, amount.getValue()); + assertEquals("USD", amount.getCurrency()); + } + + @Test + public void testDeserializeExtra() throws Exception { + final Amount amount = + ApiResource.GSON.fromJson( + "{\"value\": 10, \"currency\": \"USD\", \"extra\": 42}", Amount.class); + assertNotNull(amount); + assertEquals(10, amount.getValue()); + assertEquals("USD", amount.getCurrency()); + } + + @Test + public void testSerialize() throws Exception { + final String amountJson = ApiResource.GSON.toJson(new Amount(10, "USD"), Amount.class); + assertEquals("{\"value\":10,\"currency\":\"USD\"}", amountJson); + } +} diff --git a/src/test/java/com/stripe/v2/InstantTest.java b/src/test/java/com/stripe/v2/InstantTest.java new file mode 100644 index 00000000000..cb5e124823e --- /dev/null +++ b/src/test/java/com/stripe/v2/InstantTest.java @@ -0,0 +1,38 @@ +package com.stripe.v2; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import com.stripe.net.ApiResource; +import java.time.Instant; +import org.junit.jupiter.api.Test; + +public class InstantTest { + @Test + public void testDeserialize() { + final Instant instant = + ApiResource.GSON.fromJson("\"2023-03-01T22:08:48.920Z\"", Instant.class); + assertNotNull(instant); + assertEquals(Instant.parse("2023-03-01T22:08:48.920Z"), instant); + } + + @Test + public void testDeserializeNull() { + final Instant instant = ApiResource.GSON.fromJson("null", Instant.class); + assertNull(instant); + } + + @Test + public void testSerialize() { + final String instantJson = + ApiResource.GSON.toJson(Instant.parse("2023-03-01T22:08:48.920Z"), Instant.class); + assertEquals("\"2023-03-01T22:08:48.920Z\"", instantJson); + } + + @Test + public void testSerializeNull() { + final String instantJson = ApiResource.GSON.toJson(null, Instant.class); + assertEquals("null", instantJson); + } +} diff --git a/src/test/resources/api_fixtures/billing_meter.json b/src/test/resources/api_fixtures/billing_meter.json new file mode 100644 index 00000000000..c64443af045 --- /dev/null +++ b/src/test/resources/api_fixtures/billing_meter.json @@ -0,0 +1,25 @@ +{ + "id": "meter_123", + "object": "billing.meter", + "created": 1727303036, + "customer_mapping": { + "event_payload_key": "e1", + "type": "by_id" + }, + "default_aggregation": { + "formula": "sum" + }, + + "display_name": "API Requests", + "event_name": "API Request Made", + "event_time_window": "day", + "livemode": false, + "status": "active", + "status_transitions": { + "deactivated_at": null + }, + "updated": 1727303036, + "value_settings": { + "event_payload_key": "e1" + } +} diff --git a/src/test/resources/api_fixtures/error_v2_outbound_payment_insufficient_funds.json b/src/test/resources/api_fixtures/error_v2_outbound_payment_insufficient_funds.json new file mode 100644 index 00000000000..c235ffb09c2 --- /dev/null +++ b/src/test/resources/api_fixtures/error_v2_outbound_payment_insufficient_funds.json @@ -0,0 +1,8 @@ +{ + "error": { + "type": "temporary_session_expired", + "code": "does not matter", + "message": "Session expired", + "request_log_url": "https://dashboard.stripe.com/logs/req_xyz" + } +} diff --git a/src/test/resources/api_fixtures/external_account_collection.json b/src/test/resources/api_fixtures/external_account_collection.json index 7e30873ca1d..d2afe8ea1ca 100644 --- a/src/test/resources/api_fixtures/external_account_collection.json +++ b/src/test/resources/api_fixtures/external_account_collection.json @@ -7,7 +7,8 @@ }, { "id": "ba_123", - "object": "bank_account" + "object": "bank_account", + "account": "acc_123" }, { "id": "bar_123",