SpareSpare Docs
GuidesAPI Reference

Payments

Step-by-step guide to create a payment request, collect consent, and confirm settlement.

Integration guide

Goal: Collect a one-time bank payment from a payer and confirm when funds are settled.

Estimated time: 20 minutes

Prerequisites

  • Sandbox credentials (appId, apiKey, tenant)
  • A registered creditor bank account
  • TypeScript SDK, npm install @spare-technologies/spare-api

SDK availability

The TypeScript snippets use the published @spare-technologies/spare-api package (v1.0.0, UAE). The Python, Java, C#, and Go SDKs are still under review; for those, run this flow over the REST API and the in-browser Try-it until they ship.

Authenticate

Create a client with automatic token management:

curl -X POST https://api.sandbox.tryspare.ae/auth/api-keys/sessions \
  -H "x-tenant: UAE" \
  -H "app-id: $SPARE_APP_ID" \
  -H "x-api-key: $SPARE_API_KEY"
import { Configuration, SpareApiClient } from "@spare-technologies/spare-api";

const client = new SpareApiClient({
  authProvider: new Configuration({
    appId: process.env.SPARE_APP_ID!,
    apiKey: process.env.SPARE_API_KEY!,
    tenant: "UAE",
    environment: "sandbox",
  }),
});
import os
from spare_api import Configuration, SpareApiClient

client = SpareApiClient(
    auth_provider=Configuration(
        app_id=os.environ["SPARE_APP_ID"],
        api_key=os.environ["SPARE_API_KEY"],
        tenant="UAE",
        environment="sandbox",
    )
)
Configuration config = Configuration.builder()
    .appId(System.getenv("SPARE_APP_ID"))
    .apiKey(System.getenv("SPARE_API_KEY"))
    .tenant("UAE")
    .environment("sandbox")
    .build();

SpareApiClient client = SpareApiClient.fromConfiguration(config);
var client = new SpareApiClient(new Configuration(new ConfigurationOptions
{
    AppId       = Environment.GetEnvironmentVariable("SPARE_APP_ID")!,
    ApiKey      = Environment.GetEnvironmentVariable("SPARE_API_KEY")!,
    Tenant      = "UAE",
    Environment = "sandbox",
}));
config, err := spareapi.NewConfiguration(spareapi.ConfigurationOptions{
    AppID:       os.Getenv("SPARE_APP_ID"),
    APIKey:      os.Getenv("SPARE_API_KEY"),
    Tenant:      "UAE",
    Environment: "sandbox",
})
if err != nil {
    log.Fatal(err)
}

client := spareapi.NewClientFromConfiguration(config)

Create a payment request

Define amount, currency, and where the payer returns after bank authorization:

curl -X POST https://api.sandbox.tryspare.ae/payment-requests \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "x-tenant: UAE" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "SingleInstantPayment",
    "creditorType": "MERCHANT",
    "creditorReference": "Order #9876",
    "merchantReference": "order-9876",
    "purpose": "GDDS",
    "creditorAccount": {
      "schemeName": "IBAN",
      "identification": "AE070331234567890123456",
      "name": "Acme Trading LLC"
    },
    "instructions": {
      "amount": { "amount": "250.00", "currency": "AED" }
    },
    "successRedirectUrl": "https://yourapp.com/checkout/complete"
  }'
const request = await client.paymentRequests.create({
  amount: 250_00,
  currency: "AED",
  description: "Order #9876",
  redirectUrl: "https://yourapp.com/checkout/complete",
});
request = client.payment_requests.create(
    amount=250_00,
    currency="AED",
    description="Order #9876",
    redirect_url="https://yourapp.com/checkout/complete",
)
var request = client.paymentRequests().create(CreatePaymentRequestRequest.builder()
    .amount(250_00)
    .currency("AED")
    .description("Order #9876")
    .redirectUrl("https://yourapp.com/checkout/complete")
    .build());
var request = await client.PaymentRequests.CreateAsync(new CreatePaymentRequestRequest
{
    Amount      = 250_00,
    Currency    = "AED",
    Description = "Order #9876",
    RedirectUrl = "https://yourapp.com/checkout/complete",
});
request, err := client.PaymentRequests.Create(ctx, &spareapi.CreatePaymentRequestRequest{
    Amount:      250_00,
    Currency:    "AED",
    Description: "Order #9876",
    RedirectURL: "https://yourapp.com/checkout/complete",
})
if err != nil {
    log.Fatal(err)
}

List providers, create consent from the payment request, then redirect the browser:

const providers = await client.providers.list();

const consent = await client.paymentConsents.createFromPaymentRequest({
  paymentRequestId: request.data.id,
  provider: {
    providerId: providers.data[0].id,
    providerCode: providers.data[0].code,
  },
});

// Redirect: consent.data.authorizationUrl
providers = client.providers.list()

consent = client.payment_consents.create_from_payment_request(
    payment_request_id=request.data.id,
    provider_id=providers.data[0].id,
    provider_code=providers.data[0].code,
)

# Redirect: consent.data.authorization_url
var providers = client.providers().list();

var consent = client.paymentConsents().createFromPaymentRequest(CreateFromPaymentRequestRequest.builder()
    .paymentRequestId(request.getData().getId())
    .provider(Provider.builder()
        .providerId(providers.getData().get(0).getId())
        .providerCode(providers.getData().get(0).getCode())
        .build())
    .build());

// Redirect: consent.getData().getAuthorizationUrl()
var providers = await client.Providers.ListAsync();

var consent = await client.PaymentConsents.CreateFromPaymentRequestAsync(new CreateFromPaymentRequestRequest
{
    PaymentRequestId = request.Data.Id,
    Provider = new Provider
    {
        ProviderId   = providers.Data[0].Id,
        ProviderCode = providers.Data[0].Code,
    },
});

// Redirect: consent.Data.AuthorizationUrl
providers, err := client.Providers.List(ctx)
if err != nil {
    log.Fatal(err)
}

consent, err := client.PaymentConsents.CreateFromPaymentRequest(ctx, &spareapi.CreateFromPaymentRequestRequest{
    PaymentRequestId: request.Data.Id,
    Provider: &spareapi.Provider{
        ProviderId:   providers.Data[0].Id,
        ProviderCode: providers.Data[0].Code,
    },
})
if err != nil {
    log.Fatal(err)
}

// Redirect: consent.Data.AuthorizationUrl

Handle the callback

When the payer returns to your redirectUrl, poll consent status or wait for a webhook:

curl https://api.sandbox.tryspare.ae/consent/payment/CONSENT_ID \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "x-tenant: UAE"
const status = await client.paymentConsents.get({
  consentId: consent.data.id,
});

if (status.data.status === "Authorised") {
  // Fulfill the order; settlement follows asynchronously
}
status = client.payment_consents.get(consent_id=consent.data.id)

if status.data.status == "Authorised":
    # Fulfill the order; settlement follows asynchronously
    pass
var status = client.paymentConsents().get(consent.getData().getId());

if (status.getData().getStatus().equals("Authorised")) {
    // Fulfill the order; settlement follows asynchronously
}
var status = await client.PaymentConsents.GetAsync(consent.Data.Id);

if (status.Data.Status == "Authorised")
{
    // Fulfill the order; settlement follows asynchronously
}
status, err := client.PaymentConsents.Get(ctx, consent.Data.Id)
if err != nil {
    log.Fatal(err)
}

if status.Data.Status == "Authorised" {
    // Fulfill the order; settlement follows asynchronously
}

Confirm settlement

List payments linked to the payment request:

curl "https://api.sandbox.tryspare.ae/payments?externalReference=order-9876" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "x-tenant: UAE"
const payments = await client.payments.list({
  paymentRequestId: request.data.id,
});
payments = client.payments.list(payment_request_id=request.data.id)
var payments = client.payments().list(ListPaymentsRequest.builder()
    .paymentRequestId(request.getData().getId())
    .build());
var payments = await client.Payments.ListAsync(new ListPaymentsRequest
{
    PaymentRequestId = request.Data.Id,
});
payments, err := client.Payments.List(ctx, &spareapi.ListPaymentsRequest{
    PaymentRequestId: request.Data.Id,
})
if err != nil {
    log.Fatal(err)
}

Key takeaways

  • A payment request is your intent; a consent is payer authorization; a payment is the settled record.
  • Always redirect payers to authorizationUrl, never skip bank authorization in production.
  • Use webhooks in production instead of polling consent status indefinitely.

On this page