Appearance
Backend SDKs
Maytes ships official server-to-server SDKs so you don't hand-roll HTTP or OAuth. They're generated from a single API definition, so every language exposes the same operations and stays in sync with the API.
Available for: TypeScript, Python, PHP, Go, Java, Ruby, .NET.
Why use the SDK
- Authentication is automatic. Construct the client with your
client_id/client_secret; the SDK fetches, caches, and refreshes the access token (including retrying once on a401). You never call/oauth/token. - Typed requests and responses for every operation.
- Correct number handling — amounts stay integers, timestamps parse to your language's date type.
The SDKs are for your backend only — keep your credentials server-side.
What the SDK covers
The SDK exposes the merchant operations:
| Operation | Purpose |
|---|---|
CreateCheckout | Create a checkout |
GetCheckout | Read the order's current status |
CaptureCheckout | Capture the payment |
CancelCheckout | Void an authorization |
GetHealth | Liveness probe |
Not in the SDK
Refunds aren't an API integration — they're issued from the merchant portal. Webhook subscription management isn't in the SDK today — call those endpoints with raw HTTP and the same Bearer token, or use the portal. (Webhook deliveries to you don't involve the SDK at all — see verifying signatures.)
Construct the client
| Language | Construct the client |
|---|---|
| TypeScript | createMaytesApiClient({ endpoint, clientId, clientSecret }) |
| Python | create_maytes_api_client(endpoint, client_id, client_secret) |
| PHP | \MaytesApiClient\ClientFactory::create($endpoint, $clientId, $clientSecret) |
| Go | CreateMaytesAPIClient(endpoint, clientID, clientSecret) |
| Java | ClientFactory.create(endpoint, clientId, clientSecret) |
| Ruby | MaytesApiClient.create_maytes_api_client(endpoint:, client_id:, client_secret:) |
| .NET | MaytesApiClient.Auth.ClientFactory.Create(endpoint, clientId, clientSecret) |
Use https://api.maytes.co as the endpoint.
Example — create then capture
The same flow in each supported language: create the checkout, then capture once you learn it's authorized — via the checkout.authorized webhook in the recommended flow, or the return_url landing in the legacy flow. Capture is synchronous: when the call returns, the money has moved. To keep the snippets compact they use category: "other" line items — for richer carts (tickets with event_id etc.) see Create a checkout.
ts
import {
createMaytesApiClient,
CreateCheckoutCommand,
CaptureCheckoutCommand,
GetCheckoutCommand,
} from '@maytes/api-client';
// Construct the managed client once at startup. The SDK fetches and caches
// the OAuth token automatically and refreshes it on 401.
const maytes = createMaytesApiClient({
endpoint: 'https://api.maytes.co',
clientId: process.env.MAYTES_CLIENT_ID!,
clientSecret: process.env.MAYTES_CLIENT_SECRET!,
});
// 1. Create the checkout — call this when the customer clicks "Pay with Maytes".
// Persist `checkout_uuid` against your own `merchant_order_id`; you'll need
// it to capture once you learn the payment is authorized.
const created = await maytes.send(new CreateCheckoutCommand({
merchant_order_id: 'order_1001',
total_amount: 4500, // minor units — 4500 = AUD 45.00
currency: 'AUD',
return_url: 'https://shop.example.com/thanks?order=order_1001',
cancel_url: 'https://shop.example.com/cart',
items: [{
item_ref: 'GA-1', name: 'General Admission',
quantity: 1, unit_price: 4500, currency: 'AUD', category: 'other',
}],
}));
const uuid = created.data!.checkout_uuid;
// 2. ...customer pays in the Maytes window; your checkout.authorized
// webhook fires (or, legacy flow, the customer lands on return_url)...
// 3. Capture. Synchronous — when this returns, the money has moved.
// `expected_total_amount` and `currency` are optional assertions — Maytes
// returns 422 if they don't match the authorized amount.
const captured = await maytes.send(new CaptureCheckoutCommand({
checkoutUuid: uuid,
expected_total_amount: 4500,
currency: 'AUD',
}));
console.log(captured.data!.status); // "captured" — fulfil now
// 4. GetCheckout reads the current state whenever you need it later
// (support tooling, reconciliation, refund progress).
const status = await maytes.send(new GetCheckoutCommand({ checkoutUuid: uuid }));python
from maytes_api_client import create_maytes_api_client
from maytes_api_client.models.create_checkout_request_content import CreateCheckoutRequestContent
from maytes_api_client.models.capture_checkout_request_content import CaptureCheckoutRequestContent
from maytes_api_client.models.line_item import LineItem
# Construct the managed client once at startup. The SDK fetches and caches
# the OAuth token automatically and refreshes it on 401.
maytes = create_maytes_api_client(
"https://api.maytes.co",
client_id=MAYTES_CLIENT_ID,
client_secret=MAYTES_CLIENT_SECRET,
)
# 1. Create the checkout — call this when the customer clicks "Pay with Maytes".
# Persist `checkout_uuid` against your own `merchant_order_id`.
created = maytes.create_checkout(CreateCheckoutRequestContent(
merchant_order_id="order_1001",
total_amount=4500, # minor units — 4500 = AUD 45.00
currency="AUD",
return_url="https://shop.example.com/thanks?order=order_1001",
cancel_url="https://shop.example.com/cart",
items=[LineItem(
item_ref="GA-1", name="General Admission",
quantity=1, unit_price=4500, currency="AUD", category="other",
)],
))
uuid = created.data.checkout_uuid
# 2. ...customer pays in the Maytes window; your checkout.authorized
# webhook fires (or, legacy flow, the customer lands on return_url)...
# 3. Capture. Synchronous — when this returns, the money has moved.
# `expected_total_amount` and `currency` are optional amount assertions.
captured = maytes.capture_checkout(uuid, CaptureCheckoutRequestContent(
expected_total_amount=4500,
currency="AUD",
))
print(captured.data.status) # "captured" — fulfil now
# 4. get_checkout reads the current state whenever you need it later.
status = maytes.get_checkout(uuid)php
use MaytesApiClient\ClientFactory;
use MaytesApiClient\Model\CreateCheckoutRequestContent;
use MaytesApiClient\Model\CaptureCheckoutRequestContent;
use MaytesApiClient\Model\LineItem;
// Construct the managed client once at startup. The SDK fetches and caches
// the OAuth token automatically and refreshes it on 401.
$maytes = ClientFactory::create(
'https://api.maytes.co',
getenv('MAYTES_CLIENT_ID'),
getenv('MAYTES_CLIENT_SECRET'),
);
// 1. Create the checkout — call this when the customer clicks "Pay with Maytes".
// Persist `checkout_uuid` against your own `merchant_order_id`.
$created = $maytes->createCheckout(new CreateCheckoutRequestContent([
'merchant_order_id' => 'order_1001',
'total_amount' => 4500, // minor units — 4500 = AUD 45.00
'currency' => 'AUD',
'return_url' => 'https://shop.example.com/thanks?order=order_1001',
'cancel_url' => 'https://shop.example.com/cart',
'items' => [new LineItem([
'item_ref' => 'GA-1',
'name' => 'General Admission',
'quantity' => 1,
'unit_price' => 4500,
'currency' => 'AUD',
'category' => 'other',
])],
]));
$uuid = $created->getData()->getCheckoutUuid();
// 2. ...customer pays in the Maytes window; your checkout.authorized
// webhook fires (or, legacy flow, the customer lands on return_url)...
// 3. Capture. Synchronous — when this returns, the money has moved.
// `expected_total_amount` and `currency` are optional amount assertions.
$captured = $maytes->captureCheckout($uuid, new CaptureCheckoutRequestContent([
'expected_total_amount' => 4500,
'currency' => 'AUD',
]));
echo $captured->getData()->getStatus(); // "captured" — fulfil now
// 4. getCheckout reads the current state whenever you need it later.
$status = $maytes->getCheckout($uuid);go
import (
"context"
"fmt"
maytes "github.com/maytes/api-client-go"
)
ctx := context.Background()
// Construct the managed client once at startup. The SDK fetches and caches
// the OAuth token automatically and refreshes it on 401.
api := maytes.CreateMaytesAPIClient("https://api.maytes.co", clientID, clientSecret)
// 1. Create the checkout — call this when the customer clicks "Pay with Maytes".
// Persist `checkout_uuid` against your own `merchant_order_id`.
createReq := *maytes.NewCreateCheckoutRequestContent(
4500, "AUD", // minor units — 4500 = AUD 45.00
"https://shop.example.com/thanks?order=order_1001",
"https://shop.example.com/cart",
[]maytes.LineItem{
*maytes.NewLineItem("GA-1", "General Admission", 1, 4500, "AUD", "other"),
},
)
createReq.SetMerchantOrderId("order_1001")
created, _, err := api.DefaultAPI.CreateCheckout(ctx).
CreateCheckoutRequestContent(createReq).Execute()
if err != nil { /* handle */ }
uuid := created.Data.GetCheckoutUuid()
// 2. ...customer pays in the Maytes window; your checkout.authorized
// webhook fires (or, legacy flow, the customer lands on return_url)...
// 3. Capture. Synchronous — when this returns, the money has moved.
// `expected_total_amount` and `currency` are optional amount assertions.
captureReq := *maytes.NewCaptureCheckoutRequestContent()
captureReq.SetExpectedTotalAmount(4500)
captureReq.SetCurrency("AUD")
captured, _, err := api.DefaultAPI.CaptureCheckout(ctx, uuid).
CaptureCheckoutRequestContent(captureReq).Execute()
if err != nil { /* handle */ }
fmt.Println(captured.Data.GetStatus()) // "captured" — fulfil now
// 4. GetCheckout reads the current state whenever you need it later.
status, _, _ := api.DefaultAPI.GetCheckout(ctx, uuid).Execute()
_ = statusjava
import co.maytes.api.DefaultApi;
import co.maytes.api.auth.ClientFactory;
import co.maytes.api.model.*;
import java.util.List;
// Construct the managed client once at startup. The SDK fetches and caches
// the OAuth token automatically and refreshes it on 401.
DefaultApi maytes = ClientFactory.create(
"https://api.maytes.co",
System.getenv("MAYTES_CLIENT_ID"),
System.getenv("MAYTES_CLIENT_SECRET"));
// 1. Create the checkout — call this when the customer clicks "Pay with Maytes".
// Persist `checkout_uuid` against your own `merchant_order_id`.
CreateCheckoutResponseContent created = maytes.createCheckout(
new CreateCheckoutRequestContent()
.merchantOrderId("order_1001")
.totalAmount(4500) // minor units — 4500 = AUD 45.00
.currency("AUD")
.returnUrl("https://shop.example.com/thanks?order=order_1001")
.cancelUrl("https://shop.example.com/cart")
.items(List.of(new LineItem()
.itemRef("GA-1").name("General Admission")
.quantity(1).unitPrice(4500).currency("AUD").category("other"))));
String uuid = created.getData().getCheckoutUuid();
// 2. ...customer pays in the Maytes window; your checkout.authorized
// webhook fires (or, legacy flow, the customer lands on return_url)...
// 3. Capture. Synchronous — when this returns, the money has moved.
// `expectedTotalAmount` and `currency` are optional amount assertions.
CaptureCheckoutResponseContent captured = maytes.captureCheckout(uuid,
new CaptureCheckoutRequestContent()
.expectedTotalAmount(4500)
.currency("AUD"));
System.out.println(captured.getData().getStatus()); // "captured" — fulfil now
// 4. getCheckout reads the current state whenever you need it later.
GetCheckoutResponseContent status = maytes.getCheckout(uuid);ruby
require 'maytes_api_client'
# Construct the managed client once at startup. The SDK fetches and caches
# the OAuth token automatically and refreshes it on 401.
maytes = MaytesApiClient.create_maytes_api_client(
endpoint: 'https://api.maytes.co',
client_id: ENV['MAYTES_CLIENT_ID'],
client_secret: ENV['MAYTES_CLIENT_SECRET'],
)
# 1. Create the checkout — call this when the customer clicks "Pay with Maytes".
# Persist `checkout_uuid` against your own `merchant_order_id`.
created = maytes.create_checkout(
MaytesApiClient::CreateCheckoutRequestContent.new(
merchant_order_id: 'order_1001',
total_amount: 4500, # minor units — 4500 = AUD 45.00
currency: 'AUD',
return_url: 'https://shop.example.com/thanks?order=order_1001',
cancel_url: 'https://shop.example.com/cart',
items: [MaytesApiClient::LineItem.new(
item_ref: 'GA-1', name: 'General Admission',
quantity: 1, unit_price: 4500, currency: 'AUD', category: 'other',
)],
),
)
uuid = created.data.checkout_uuid
# 2. ...customer pays in the Maytes window; your checkout.authorized
# webhook fires (or, legacy flow, the customer lands on return_url)...
# 3. Capture. Synchronous — when this returns, the money has moved.
# All capture fields are optional in the Smithy model, so the Ruby
# generator passes the body as a keyword arg.
captured = maytes.capture_checkout(uuid,
capture_checkout_request_content: MaytesApiClient::CaptureCheckoutRequestContent.new(
expected_total_amount: 4500, currency: 'AUD',
),
)
puts captured.data.status # "captured" — fulfil now
# 4. get_checkout reads the current state whenever you need it later.
status = maytes.get_checkout(uuid)csharp
using MaytesApiClient.Auth;
using MaytesApiClient.Model;
// Construct the managed client once at startup. The SDK fetches and caches
// the OAuth token automatically and refreshes it on 401.
var maytes = ClientFactory.Create(
"https://api.maytes.co",
Environment.GetEnvironmentVariable("MAYTES_CLIENT_ID"),
Environment.GetEnvironmentVariable("MAYTES_CLIENT_SECRET"));
// 1. Create the checkout — call this when the customer clicks "Pay with Maytes".
// Persist `checkout_uuid` against your own `merchant_order_id`.
var created = await maytes.CreateCheckoutAsync(new CreateCheckoutRequestContent(
totalAmount: 4500, // minor units — 4500 = AUD 45.00
currency: "AUD",
returnUrl: "https://shop.example.com/thanks?order=order_1001",
cancelUrl: "https://shop.example.com/cart",
items: new List<LineItem>
{
new LineItem(itemRef: "GA-1", name: "General Admission",
quantity: 1, unitPrice: 4500, currency: "AUD", category: "other"),
})
{
MerchantOrderId = "order_1001",
});
var uuid = created.Data.CheckoutUuid;
// 2. ...customer pays in the Maytes window; your checkout.authorized
// webhook fires (or, legacy flow, the customer lands on return_url)...
// 3. Capture. Synchronous — when this returns, the money has moved.
// `ExpectedTotalAmount` and `Currency` are optional amount assertions.
var captured = await maytes.CaptureCheckoutAsync(uuid, new CaptureCheckoutRequestContent
{
ExpectedTotalAmount = 4500,
Currency = "AUD",
});
Console.WriteLine(captured.Data.Status); // "captured" — fulfil now
// 4. GetCheckout reads the current state whenever you need it later.
var status = await maytes.GetCheckoutAsync(uuid);Authentication, the SDK way
You never call the token endpoint. The SDK mints a token on the first call, caches it, and on a 401 refreshes once and retries the request transparently. See Authentication for the underlying flow.
Next steps
- Create a checkout — full request reference.
- Webhooks — the
checkout.authorizedevent that triggers capture. - Capture & order status — finalise the order and read its state.
- API reference — endpoints and errors.
