Skip to content

Commit

Permalink
Adding support for the new Usage Billing APIs (#1873)
Browse files Browse the repository at this point in the history
  • Loading branch information
prathmesh-stripe authored Sep 27, 2024
1 parent 265dcb5 commit 5cd2f1b
Show file tree
Hide file tree
Showing 101 changed files with 5,162 additions and 360 deletions.
6 changes: 5 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

}
}
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String> 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.
Expand Down
19 changes: 16 additions & 3 deletions src/main/java/com/stripe/Stripe.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,27 @@ 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
// fall back to a default.
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<String, String> appInfo = null;

/**
Expand Down Expand Up @@ -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.
*
Expand All @@ -94,6 +106,7 @@ public static int getConnectTimeout() {
if (connectTimeout == -1) {
return DEFAULT_CONNECT_TIMEOUT;
}

return connectTimeout;
}

Expand Down
176 changes: 152 additions & 24 deletions src/main/java/com/stripe/StripeClient.java
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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;
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -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());
}
Expand All @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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;
Expand All @@ -404,6 +438,8 @@ static class ClientStripeResponseGetterOptions extends StripeResponseGetterOptio
this.apiBase = apiBase;
this.filesBase = filesBase;
this.connectBase = connectBase;
this.meterEventsBase = meterEventsBase;
this.stripeContext = stripeContext;
}
}

Expand All @@ -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;
Expand All @@ -426,24 +462,43 @@ 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
* default values.
*/
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;
}

Expand Down Expand Up @@ -575,22 +630,42 @@ public StripeClientBuilder setConnectBase(String address) {
return this;
}

public String getConnectBase() {
return this.connectBase;
/**
* Set the base URL for the Stripe Meter Events API. By default this is
* "https://events.stripe.com".
*
* <p>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 StripeClient} with the specified configuration. */
/** 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,
Expand All @@ -599,7 +674,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) throws StripeException {
return StripeObject.deserializeStripeObject(rawJson, this.getResponseGetter());
}
}
Loading

0 comments on commit 5cd2f1b

Please sign in to comment.