Version 41.0.0
of the .NET SDK represents a full rewrite of the SDK with a number of breaking changes, outlined in the following sections. When upgrading to this version or later versions, take note of the changes in the SDK, including client construction and parameter names. If necessary, you can use the legacy version of the SDK along with the latest version.
SDK versions 40.1.0
and earlier continue to function as expected, but you should plan to update your integration as soon as possible to ensure continued support.
To create a new client, you construct it using new
and passing in parameters, as opposed to the legacy client that uses a Java-esque builder pattern.
using Square;
// new
var client = new SquareClient(
token, // optional if you configure SQUARE_TOKEN environment variable
new ClientOptions
{
BaseUrl = SquareEnvironment.Production // default, so you can omit ClientOptions parameter
}
);
// legacy
var legacyClient = new Square.Legacy.SquareClient.Builder()
.BearerAuthCredentials(new BearerAuthModel.Builder(token).Build())
.Environment(Square.Legacy.Environment.Production)
.Build();
Legacy | New | Additional information |
---|---|---|
AccessToken | Token | The access token used for authentication. |
BearerAuthCredentials | Removed | Set using the Token or SQUARE_TOKEN environment variable. |
CustomUrl | ClientOptions.BaseUrl | Sets the base URL for API requests and defaults to https://connect.squareup.com . |
SquareVersion | ClientOptions.Version | The Square API version to use. |
AdditionalHeaders | ClientOptions.AdditionalHeaders | Available through ClientOptions.AdditionalHeaders . |
UserAgentDetail | Removed | Set the user agent header on ClientOptions.DefaultHeaders . |
Timeout | Timeout | Accepts a TimeSpan and the default is 30 seconds. |
HttpCallback | Removed | Configure the HTTP client and options on the ClientOptions . |
HttpClientConfig | Removed | Configure the HTTP client and options on the ClientOptions . |
While the new SDK has many improvements, it takes time to upgrade when there are breaking changes. To make the migration easier, the old SDK is available in the Square.Legacy
NuGet package. The following example shows how to use the new and legacy SDKs inside a single file:
using Square;
using Square.Legacy.Authentication;
using Square.Locations;
public class Program
{
public static async Task Main(string[] args)
{
var token = Environment.GetEnvironmentVariable("SQUARE_TOKEN") ??
throw new Exception("Missing SQUARE_TOKEN environment variable");
// LEGACY
var legacyClient = new Square.Legacy.SquareClient.Builder()
.BearerAuthCredentials(new BearerAuthModel.Builder(token).Build())
.Environment(Square.Legacy.Environment.Sandbox)
.Build();
// NEW
var client = new SquareClient(
token,
new ClientOptions
{
BaseUrl = SquareEnvironment.Sandbox
}
);
// use new client
var location = await GetLocationAsync(client);
// use legacy client
await CreateOrderAsync(legacyClient, location);
}
private static async Task<Location> GetLocationAsync(SquareClient client)
{
var response = await client.Locations.GetAsync(
new GetLocationsRequest
{
LocationId = "YOUR_LOCATION_ID"
}
);
return response.Location ?? throw new Exception("response.Location is null");
}
private static async Task CreateOrderAsync(
Square.Legacy.SquareClient legacyClient,
Location location
)
{
await legacyClient
.OrdersApi
.CreateOrderAsync(new Square.Legacy.Models.CreateOrderRequest.Builder()
.IdempotencyKey(Guid.NewGuid().ToString())
.Order(new Square.Legacy.Models.Order.Builder(location.Id).Build())
.Build());
}
}
You should migrate to the new SDK using the following steps:
- Install the
Square.Legacy
NuGet package alongside the existing Square SDK. - Search and replace all
using
statements fromSquare
toSquare.Legacy
. - Gradually introduce the new SDK by importing it from the
Square
namespace.
The legacy SDK allows you to instantiate a request either using a constructor and positional parameters or using a builder pattern. With the new SDK, you simply create a new instance of the request and use the property initializer to set the properties.
// Legacy with builder pattern
var request = new Square.Legacy.Models.CreatePaymentRequest.Builder("SOURCE_ID", "IDEMPOTENCY_KEY")
.AmountMoney(new Square.Legacy.Models.Money.Builder().Amount(20L).Currency("USD").Build())
.TipMoney(new Square.Legacy.Models.Money.Builder().Amount(3L).Currency("USD").Build())
.Build();
// Legacy with positional constructor parameters
request = new Square.Legacy.Models.CreatePaymentRequest(
"SOURCE_ID",
Guid.NewGuid().ToString(),
new Square.Legacy.Models.Money(20L, "USD"),
new Square.Legacy.Models.Money(3L, "USD")
);
// New with property initializers
var request = new CreatePaymentRequest
{
SourceId = "SOURCE_ID",
IdempotencyKey = Guid.NewGuid().ToString(),
AmountMoney = new Money
{
Amount = 20L,
Currency = Currency.Usd
},
TipMoney = new Money
{
Amount = 3L,
Currency = Currency.Usd
}
};
Previously, API classes were named after the resource you're interacting with and suffixed with Api
. To interact with customer resources, you use the client.CustomersApi
property. Methods were named based on the action you want to perform and the same resource name specified earlier. To list customers, you call client.CustomersApi.ListCustomersAsync()
.
// Legacy
client.CustomersApi.ListCustomersAsync()
The new API classes are named by the resource without the Api
suffix, and the methods now name the action without mentioning the resource a second time.
// New
client.Customers.ListAsync()
When interacting with a nested resource, you can chain the resources using dot-notation.
// New
client.Customers.Groups.ListAsync();
Some other class and method names might have changed. The differences are easiest to discover by exploring the new method signatures.
Square's paginated endpoints now support auto-pagination with an iterator. Callers don’t need to manually fetch the next page. You can now iterate over the entire list and the client automatically fetches the next page behind the scenes.
The following example shows the same code, with the legacy and new SDKs:
// Legacy
using Square.Legacy;
using Square.Legacy.Authentication;
var legacyClient = new SquareClient.Builder()
.BearerAuthCredentials(new BearerAuthModel.Builder("YOUR_TOKEN").Build())
.Build();
string? cursor = null;
do
{
var response = await legacyClient.PaymentsApi.ListPaymentsAsync(total: 100, cursor: cursor);
if (response.Payments is null) break;
var payments = response.Payments;
foreach (var payment in payments)
{
Console.WriteLine(
"payment: ID: {0}, Created at: {1}, Updated at: {2}",
payment.Id,
payment.CreatedAt,
payment.UpdatedAt
);
}
cursor = response.Cursor;
} while (!string.IsNullOrEmpty(cursor));
// New
using Square;
using Square.Payments;
var client = new SquareClient("YOUR_TOKEN");
var payments = await client.Payments.ListAsync(new ListPaymentsRequest{ Limit = 100 });
await foreach (var payment in payments)
{
Console.WriteLine(
"payment: ID: {0}, Created at: {1}, Updated at: {2}",
payment.Id,
payment.CreatedAt,
payment.UpdatedAt
);
}
If you need to paginate page by page, you can still do so.
await foreach (var page in payments.AsPagesAsync())
{
foreach (var payment in page.Items)
{
Console.WriteLine(
"payment: ID: {0}, Created at: {1}, Updated at: {2}",
payment.Id,
payment.CreatedAt,
payment.UpdatedAt
);
}
}
In the legacy SDK, you can make synchronous calls. Following modern .NET standards, synchronous API calls are no longer supported.
In the legacy SDK, explicit null values can be sent using null
values. The new SDK uses RequestOptions.AdditionalBodyProperties
.
// Legacy
await legacyClient.LocationsApi.UpdateLocationAsync(
locationId,
new Square.Legacy.Models.UpdateLocationRequest.Builder()
.Location(new Square.Legacy.Models.Location.Builder()
.WebsiteUrl("https://developer.squareup.com")
.TwitterUsername(null)
.Build()
).Build()
);
// New
await client.Locations.UpdateAsync(
new UpdateLocationRequest
{
LocationId = locationId,
Location = new Location
{
WebsiteUrl = "https://developer.squareup.com"
}
},
new RequestOptions
{
AdditionalBodyProperties = new
{
location = new Dictionary<string, object?>
{
["twitter_username"] = null
}
}
}
);
Note
You can pass anything into AdditionalBodyProperties
that translates to a JSON object, but you need to use a dictionary to ensure null
values aren't omitted.