Linkit

Providers Paylinks API

Unified payment link management across all BNPL and direct payment providers

Unified Providers Paylinks API

A single, provider-agnostic API for BNPL payment links (Tabby, Tamara, MisPay). GET /api/v1/providers also lists gateway sync processors (stripe, moyasar, tap, dinero, paypal, hyperpay); those IDs are not valid for paylink create — use Payments Orders instead.

Live on the Rust host (DV-73): GET /providers, GET /providers/config/{provider}, list/get/cancel paylinks, create, and capture all answer. GET /providers/{provider}/config is not registered (mux 404). GET …/paylinks/{id} polls Tabby / Tamara / MisPay the way Go does; a vendor error is swallowed and the stored row is still 200. No live BNPL in the differential corpus — vendor I/O is a recorded stub. Auth still runs first.

Served endpoints require a Bearer token. Errors on this family use the platform envelope ({"data":{},"message":"…","status":N}), sentenized, not {"error","code"}.


Supported Providers

ProviderTypeCheckout / roleRegions
tabbybnplQR code paylinksKSA, UAE, Kuwait
tamarabnplSMS paylinksKSA, UAE, Bahrain
mispaybnplQR code paylinksKSA
stripegateway_syncStripe PaymentIntents sync (stripe-payments)Global (account)
moyasargateway_syncMoyasar payments sync (moyasar-payments)MENA
tapgateway_syncTap charges list sync (tap-payments)GCC / Tap markets
dinerogateway_syncDinero status refresh (dinero-payments)KSA / Dinero markets
paypalgateway_syncPayPal reporting sync (paypal-commerce)Global (account)
hyperpaygateway_syncOPPWA query sync (hyperpay-checkout)Per acquirer / host

List Providers

Returns all registered payment providers and their metadata.

GET /api/v1/providers
Authorization: Bearer <token>

Response

Each entry includes id, name, description, type, regions, and when applicable app_slug (integration app used by Payments Orders).

{
  "providers": [
    {
      "id": "dinero",
      "name": "Dinero Pay",
      "description": "Dinero Pay: refresh configured payment_ids…",
      "type": "gateway_sync",
      "regions": "KSA and supported Dinero markets",
      "app_slug": "dinero-payments"
    },
    {
      "id": "mispay",
      "name": "MisPay",
      "description": "Split-in-4 BNPL with QR code checkout for POS",
      "type": "bnpl",
      "regions": "KSA",
      "app_slug": "mispay-pos-paylinks"
    }
  ],
  "total": 9
}

Provider Configuration

Returns provider-specific configuration, required fields, and endpoint documentation.

GET /api/v1/providers/config/{provider}
Authorization: Bearer <token>

POST /api/v1/providers/paylinks
Authorization: Bearer <token>
Content-Type: application/json

Served. Create talks to Tabby / Tamara / MisPay through PaylinkVendorPort (PgPaylinkVendor on the live money bundle). Gateway-sync ids (stripe, paypal, …) stay 400. Differential verification used a recorded httptest stub — no live BNPL.

The field table below is the Go contract (GET /providers/config/{provider} still lists these required_fields).

Common Fields

FieldTypeRequiredDescription
providerstring✅BNPL provider ID only: tabby, tamara, mispay (not stripe / moyasar / tap / dinero)
org_app_idstring✅Organization app ID for the installed provider app
order_idstring✅Your order reference ID
amountstring/number✅Payment amount (e.g. "100.00")
currencystring✅Currency code (SAR, AED, KWD)
descriptionstringOrder description
buyer_namestringCustomer name
buyer_emailstringCustomer email
buyer_phonestringCustomer phone
langstringCheckout language (ar or en)
itemsarrayLine items
metadataobjectProvider-specific metadata

Provider-Specific Fields

Tamara

FieldTypeDescription
phone_numberstringRequired. Customer phone for SMS checkout
payment_typestringpay_by_instalments (default) or pay_by_later
store_codestringStore identifier for multi-store setups
localestringLocale for checkout page (defaults to ar_SA)

The React App Store Tabby step lists and cancels existing rows. It does not offer create.


GET /api/v1/providers/paylinks/{id}
Authorization: Bearer <token>

Retrieves the stored row after Go's refreshProviderPaylinkStatus poll (Tabby RetrievePayment, Tamara GetOrderDetails, MisPay TrackCheckout). A vendor error is swallowed and the stored row is still 200. A missing or cross-tenant id is 404 Payment link not found. (not an existence oracle). Amount and the other scalar fields are strings. List and get share this shape. List does not refresh.

Response

{
  "id": "abc123",
  "provider": "tabby",
  "org_app_id": "...",
  "order_id": "POS-1234",
  "status": "authorized",
  "amount": "150.00",
  "currency": "SAR",
  "checkout_url": "https://checkout.tabby.sa/...",
  "qr_code_url": "https://checkout.tabby.sa/qr/..."
}

GET /api/v1/providers/paylinks?provider=tabby&org_app_id=YOUR_ORG_APP_ID
Authorization: Bearer <token>

{"items":[…],"total":n}. Optional query parameters:

  • provider — Filter by provider (e.g. tabby, tamara, mispay)
  • org_app_id — Filter by organization app

An empty list is also the answer when the org has no installs, the requested org_app_id is not one of them, or the query failed. A client cannot tell those apart.


Capture/Finalize Payment

POST /api/v1/providers/paylinks/{id}/capture
Authorization: Bearer <token>
Content-Type: application/json

Served. Capture / finalize talks to the same vendor port. A missing or cross-tenant id is 404 Payment link not found. MisPay needs a checkout_id (GET refresh can fill it after create).


Cancels a pending payment link. Served. The local row is set to cancelled whether or not the vendor cancel succeeds (Tabby close / Tamara void+cancel errors are discarded; MisPay has no remote cancel). A missing id is 404 Payment link not found.

POST /api/v1/providers/paylinks/{id}/cancel
Authorization: Bearer <token>

Response

{
  "success": true,
  "paylink_id": "abc123",
  "provider": "tabby",
  "status": "cancelled"
}

Provider-specific endpoints

There are none. Every payment link operation goes through the unified /api/v1/providers/paylinks endpoints with "provider" in the body or query, and provider configuration is GET /api/v1/providers/config/{provider}.


POS Integration Flow

POS flow is create → display QR/SMS → wait → webhook → capture, same as Go:

  1. Create — POST /api/v1/providers/paylinks (BNPL ids only)
  2. List / get — cashier tracks rows; GET {id} may refresh vendor status
  3. Cancel — POST …/{id}/cancel marks the local row cancelled
  4. Capture — POST …/{id}/capture after the vendor session is payable