Skip to main content

Payments Integration API (1.0.0)

API contract for external payment integrations.

Third-party integrators implement this API as a standalone REST service. LoyaltyPlant (payments-service) calls these endpoints to delegate payment operations to your integration; your service in turn talks to your PSP (payment service provider). You never call payments-service to run a payment — payments-service drives the flow by calling you.

Payment Operations

Core payment lifecycle operations

Authorize Payment

Place a hold on the customer's funds without capturing them (first leg of a two-phase payment). payments-service calls this when your integration declares the CAPTURE capability; settle later with /v1/capture, or release with /v1/reverse.

Return SUCCESS with a transactionReference on an immediate approval, PENDING + redirectUrl if 3DS/redirect is required (see the integrationResult callback), or DECLINED/RETRIABLE/UNKNOWN. Idempotent on Idempotency-Key.

If paymentInstrument is present, charge that instrument — an enrolled card token or an Apple Pay / Google Pay payload. It appears only if you declared the matching capability, and is absent for integrations that declared none, so existing behaviour is unchanged.

header Parameters
Authorization
required
string

Bearer token authenticating payments-service to your integration (the outbound token LoyaltyPlant provisions for you during onboarding). Validate it on every request (constant-time compare) and reject anything else with 401. Treat the token as a secret; rotate it via LoyaltyPlant. Example: "Bearer sk_live_abc123".

Idempotency-Key
required
string <uuid>

UUID for idempotent request handling, unique per logical operation. Carried by all mutating operations (authorize, sale, capture, refund, reverse); the read-only operations (inquire-status, capabilities, health) do not carry it. Your integration MUST return the same response for repeated requests carrying the same key (payments-service retries transient/network failures and must never double-charge).

Request Body schema: application/json
required
paymentId
required
string

Unique payment identifier from payments-service. Echo it back in logs; use it to correlate retries.

amount
required
number

Payment amount in major currency units (e.g. 49.99 = 49 dollars 99 cents). Convert to your PSP's required unit (e.g. minor units / cents) yourself.

currency
required
string

ISO 4217 currency code (e.g. "USD", "EUR", "SAR")

object

Partner/outlet-specific configuration provisioned by LoyaltyPlant (NOT chosen by the integrator). Common keys: currency (always present), merchantId. Any extra keys your PSP needs are agreed during onboarding and passed through verbatim.

object (PaymentInstrument)

The per-payment instrument to charge. Optional: when the field is absent, behave exactly as before — nothing changes for integrations that do not declare TOKENIZATION / APPLE_PAY / GOOGLE_PAY.

Which field carries the payload depends on type:

type Field to read Requires capability
CARD_TOKEN token TOKENIZATION
APPLE_PAY walletPayload APPLE_PAY
GOOGLE_PAY walletPayload GOOGLE_PAY

payments-service only sends an instrument whose capability you declared, so you never have to handle a type you did not opt into.

Both token and walletPayload are secrets: never write them to logs, never echo them back in reason, never persist them outside your PSP integration.

Responses

Callbacks

Request samples

Content type
application/json
Example
{
  • "paymentId": "8e2b7d6a-1f3c-4e9a-9c2b-77a0e3d4f111",
  • "amount": 49.99,
  • "currency": "USD",
  • "settings": {
    }
}

Response samples

Content type
application/json
Example
{
  • "status": "SUCCESS",
  • "transactionReference": "psp_txn_7HagaIn2"
}

Callback payload samples

Callback
POST: Report async payment result to payments-service (vendor → LoyaltyPlant)
Content type
application/json
Example
{
  • "status": "SUCCESS",
  • "transactionReference": "psp_txn_7HagaIn2"
}

Capture Authorized Payment

Settle (capture) funds previously held by /v1/authorize. Requires the CAPTURE capability. transactionReference is the value you returned from authorize. amount is optional — omit it to capture the full authorized amount, or pass a smaller value for a partial capture. Idempotent on Idempotency-Key.

header Parameters
Authorization
required
string

Bearer token authenticating payments-service to your integration (the outbound token LoyaltyPlant provisions for you during onboarding). Validate it on every request (constant-time compare) and reject anything else with 401. Treat the token as a secret; rotate it via LoyaltyPlant. Example: "Bearer sk_live_abc123".

Idempotency-Key
required
string <uuid>

UUID for idempotent request handling, unique per logical operation. Carried by all mutating operations (authorize, sale, capture, refund, reverse); the read-only operations (inquire-status, capabilities, health) do not carry it. Your integration MUST return the same response for repeated requests carrying the same key (payments-service retries transient/network failures and must never double-charge).

Request Body schema: application/json
required
paymentId
required
string

Unique payment identifier from payments-service

transactionReference
required
string

Transaction reference you returned from authorize

amount
number

Capture amount (optional, for partial capture; if omitted, captures the full authorized amount)

object

Partner/outlet-specific configuration provisioned by LoyaltyPlant (NOT chosen by the integrator)

Responses

Request samples

Content type
application/json
{
  • "paymentId": "8e2b7d6a-1f3c-4e9a-9c2b-77a0e3d4f111",
  • "transactionReference": "psp_txn_7Hagain2",
  • "amount": 49.99,
  • "settings": {
    }
}

Response samples

Content type
application/json
Example
{
  • "status": "SUCCESS",
  • "transactionReference": "psp_txn_7HagaIn2"
}

Sale Payment (Authorize + Capture in one step)

Authorize and capture in a single call (single-phase payment). payments-service calls this when your integration declares the SALE capability. Return SUCCESS with a transactionReference, PENDING + redirectUrl for 3DS/redirect (see the integrationResult callback), or DECLINED/RETRIABLE/UNKNOWN. Idempotent on Idempotency-Key.

paymentInstrument behaves exactly as in /v1/authorize: charge the enrolled card token or wallet payload when it is present.

header Parameters
Authorization
required
string

Bearer token authenticating payments-service to your integration (the outbound token LoyaltyPlant provisions for you during onboarding). Validate it on every request (constant-time compare) and reject anything else with 401. Treat the token as a secret; rotate it via LoyaltyPlant. Example: "Bearer sk_live_abc123".

Idempotency-Key
required
string <uuid>

UUID for idempotent request handling, unique per logical operation. Carried by all mutating operations (authorize, sale, capture, refund, reverse); the read-only operations (inquire-status, capabilities, health) do not carry it. Your integration MUST return the same response for repeated requests carrying the same key (payments-service retries transient/network failures and must never double-charge).

Request Body schema: application/json
required
paymentId
required
string

Unique payment identifier from payments-service

amount
required
number

Payment amount in major currency units (e.g. 49.99). Convert to your PSP's required unit yourself.

currency
required
string

ISO 4217 currency code (e.g. "USD", "EUR", "SAR")

object

Partner/outlet-specific configuration provisioned by LoyaltyPlant (NOT chosen by the integrator). Common keys: currency (always present), merchantId.

object (PaymentInstrument)

The per-payment instrument to charge. Optional: when the field is absent, behave exactly as before — nothing changes for integrations that do not declare TOKENIZATION / APPLE_PAY / GOOGLE_PAY.

Which field carries the payload depends on type:

type Field to read Requires capability
CARD_TOKEN token TOKENIZATION
APPLE_PAY walletPayload APPLE_PAY
GOOGLE_PAY walletPayload GOOGLE_PAY

payments-service only sends an instrument whose capability you declared, so you never have to handle a type you did not opt into.

Both token and walletPayload are secrets: never write them to logs, never echo them back in reason, never persist them outside your PSP integration.

Responses

Callbacks

Request samples

Content type
application/json
{
  • "paymentId": "8e2b7d6a-1f3c-4e9a-9c2b-77a0e3d4f111",
  • "amount": 49.99,
  • "currency": "USD",
  • "settings": {
    }
}

Response samples

Content type
application/json
Example
{
  • "status": "SUCCESS",
  • "transactionReference": "psp_txn_7HagaIn2"
}

Callback payload samples

Callback
POST: Report async payment result to payments-service (vendor → LoyaltyPlant)
Content type
application/json
Example
{
  • "status": "SUCCESS",
  • "transactionReference": "psp_txn_7HagaIn2"
}

Refund Payment

Return funds for a captured payment. Requires the DIRECT_REFUND capability. transactionReference identifies the original payment; amount is the amount to refund (supports partial refunds). Idempotent on Idempotency-Key.

header Parameters
Authorization
required
string

Bearer token authenticating payments-service to your integration (the outbound token LoyaltyPlant provisions for you during onboarding). Validate it on every request (constant-time compare) and reject anything else with 401. Treat the token as a secret; rotate it via LoyaltyPlant. Example: "Bearer sk_live_abc123".

Idempotency-Key
required
string <uuid>

UUID for idempotent request handling, unique per logical operation. Carried by all mutating operations (authorize, sale, capture, refund, reverse); the read-only operations (inquire-status, capabilities, health) do not carry it. Your integration MUST return the same response for repeated requests carrying the same key (payments-service retries transient/network failures and must never double-charge).

Request Body schema: application/json
required
paymentId
required
string

Unique payment identifier from payments-service

transactionReference
required
string

Transaction reference of the payment to refund

amount
required
number

Refund amount in major currency units (supports partial refunds)

reason
string

Reason for the refund (free text, for your records / PSP)

object

Partner/outlet-specific configuration provisioned by LoyaltyPlant (NOT chosen by the integrator)

Responses

Request samples

Content type
application/json
{
  • "paymentId": "8e2b7d6a-1f3c-4e9a-9c2b-77a0e3d4f111",
  • "transactionReference": "psp_txn_7Hagain2",
  • "amount": 10,
  • "reason": "Item out of stock",
  • "settings": {
    }
}

Response samples

Content type
application/json
Example
{
  • "status": "SUCCESS",
  • "transactionReference": "psp_txn_7HagaIn2"
}

Reverse/Void Payment

Void an authorization before it is captured (releases the hold). transactionReference is the value returned from authorize. Idempotent on Idempotency-Key.

header Parameters
Authorization
required
string

Bearer token authenticating payments-service to your integration (the outbound token LoyaltyPlant provisions for you during onboarding). Validate it on every request (constant-time compare) and reject anything else with 401. Treat the token as a secret; rotate it via LoyaltyPlant. Example: "Bearer sk_live_abc123".

Idempotency-Key
required
string <uuid>

UUID for idempotent request handling, unique per logical operation. Carried by all mutating operations (authorize, sale, capture, refund, reverse); the read-only operations (inquire-status, capabilities, health) do not carry it. Your integration MUST return the same response for repeated requests carrying the same key (payments-service retries transient/network failures and must never double-charge).

Request Body schema: application/json
required
paymentId
required
string

Unique payment identifier from payments-service

transactionReference
required
string

Transaction reference of the payment to reverse/void

object

Partner/outlet-specific configuration provisioned by LoyaltyPlant (NOT chosen by the integrator)

Responses

Request samples

Content type
application/json
{
  • "paymentId": "8e2b7d6a-1f3c-4e9a-9c2b-77a0e3d4f111",
  • "transactionReference": "psp_txn_7Hagain2",
  • "settings": {
    }
}

Response samples

Content type
application/json
Example
{
  • "status": "SUCCESS",
  • "transactionReference": "psp_txn_7HagaIn2"
}

Inquire Payment Status from PSP

Queries the PSP for the current status of a previously initiated payment. Used for reconciliation and manual status checks (e.g. after a timeout or an UNKNOWN result, or when an async callback never arrived). Requires STATUS_POLLING capability. This is a read-only operation and carries no Idempotency-Key.

header Parameters
Authorization
required
string

Bearer token authenticating payments-service to your integration (the outbound token LoyaltyPlant provisions for you during onboarding). Validate it on every request (constant-time compare) and reject anything else with 401. Treat the token as a secret; rotate it via LoyaltyPlant. Example: "Bearer sk_live_abc123".

Request Body schema: application/json
required
transactionReference
string

Transaction reference returned by a previous authorize/sale/capture operation

paymentId
string

Payment ID from payments-service (alternative lookup key)

object

Partner/outlet-specific configuration provisioned by LoyaltyPlant (NOT chosen by the integrator)

Responses

Request samples

Content type
application/json
{
  • "transactionReference": "psp_txn_7Hain2",
  • "paymentId": "8e2b7d6a-1f3c-4e9a-9c2b-77a0e3d4f111",
  • "settings": {
    }
}

Response samples

Content type
application/json
{
  • "status": "CAPTURED",
  • "transactionReference": "psp_txn_7HagaIn2",
  • "rawStatus": "captured"
}

Card Enrollment

Card tokenization — binding a card for later one-click payments

Enroll (tokenize) a Card

Start binding a customer's card so it can be charged later without re-entering card details. payments-service calls this when the customer adds a card in the app, and only when your integration declares the TOKENIZATION capability.

Two shapes of answer are valid:

  • PENDING + redirectUrl + transactionReference — the usual case. redirectUrl is your hosted card-entry page; payments-service opens it for the customer. When the PSP tells you the card is bound, deliver the token with the integrationEnrollmentResult callback.
  • SUCCESS + card — for PSPs that tokenize without a redirect (e.g. client-side SDK tokenization). No callback follows.

DECLINED / RETRIABLE / UNKNOWN behave exactly as in payment operations. Idempotent on Idempotency-Key.

Never return the card token on the browser redirect to returnUrls — those are navigation only. The token travels server-to-server, in the callback or in this response.

header Parameters
Authorization
required
string

Bearer token authenticating payments-service to your integration (the outbound token LoyaltyPlant provisions for you during onboarding). Validate it on every request (constant-time compare) and reject anything else with 401. Treat the token as a secret; rotate it via LoyaltyPlant. Example: "Bearer sk_live_abc123".

Idempotency-Key
required
string <uuid>

UUID for idempotent request handling, unique per logical operation. Carried by all mutating operations (authorize, sale, capture, refund, reverse); the read-only operations (inquire-status, capabilities, health) do not carry it. Your integration MUST return the same response for repeated requests carrying the same key (payments-service retries transient/network failures and must never double-charge).

Request Body schema: application/json
required
enrollmentId
required
string <uuid>

Enrollment identifier minted by payments-service. Echo it back in the integrationEnrollmentResult callback and accept it in /v1/inquire-enrollment-status. It is the only key guaranteed to be known on both sides.

object (EnrollmentCustomer)

Customer details for the hosted card-entry page. Whether any of these are required depends on your PSP — state your requirements during onboarding. LoyaltyPlant never sends a customer identifier here: your service does not need to know who the customer is, and payments-service resolves the enrollment from enrollmentId / transactionReference on the way back.

object (EnrollmentReturnUrls)

Where your hosted page sends the browser once the customer is done. These are navigation only — payments-service treats them as UX, not as a result. Never append the card token or any PSP secret to them.

object

Partner/outlet-specific configuration provisioned by LoyaltyPlant (NOT chosen by the integrator). Common keys: currency (always present), merchantId.

Responses

Callbacks

Request samples

Content type
application/json
{}

Response samples

Content type
application/json
Example
{}

Callback payload samples

Callback
POST: Deliver a card token to payments-service (vendor → LoyaltyPlant)
Content type
application/json
Example
{
  • "enrollmentId": "3f6c1b90-2d4e-4a11-9f77-0b2c5d8e4a10",
  • "transactionReference": "psp_enr_9KdlaQ7",
  • "status": "SUCCESS",
  • "card": {
    }
}

Inquire Enrollment Status from PSP

Look up the current state of an enrollment started with /v1/enroll. payments-service calls this once before expiring a pending enrollment, to recover from a lost integrationEnrollmentResult callback: without it a card bound at your PSP would silently never appear for the customer.

Requires the STATUS_POLLING capability (the same flag that gates /v1/inquire-status). Read-only, no Idempotency-Key.

Look up by enrollmentId — it is always known to payments-service, whereas transactionReference is not if your PENDING response was lost in transit. Return the card object whenever the status is COMPLETED.

header Parameters
Authorization
required
string

Bearer token authenticating payments-service to your integration (the outbound token LoyaltyPlant provisions for you during onboarding). Validate it on every request (constant-time compare) and reject anything else with 401. Treat the token as a secret; rotate it via LoyaltyPlant. Example: "Bearer sk_live_abc123".

Request Body schema: application/json
required
enrollmentId
required
string <uuid>

Enrollment identifier from the original /v1/enroll request.

transactionReference
string

The reference you returned in the PENDING response, when payments-service has it. Absent if your PENDING response never arrived — resolve by enrollmentId in that case.

Responses

Request samples

Content type
application/json
{
  • "enrollmentId": "3f6c1b90-2d4e-4a11-9f77-0b2c5d8e4a10",
  • "transactionReference": "psp_enr_9KdlaQ7"
}

Response samples

Content type
application/json
{
  • "status": "COMPLETED",
  • "transactionReference": "psp_enr_9KdlaQ7",
  • "card": {
    },
  • "rawStatus": "card_verified"
}

Integration Management

Capability declaration and health monitoring

Get Supported Capabilities

Returns the list of payment capabilities this integration supports. Payments-service caches the result and validates capabilities before invoking optional operations — it will never call an operation whose capability you did not declare. Declare only what you have actually implemented and tested.

header Parameters
Authorization
required
string

Bearer token authenticating payments-service to your integration (the outbound token LoyaltyPlant provisions for you during onboarding). Validate it on every request (constant-time compare) and reject anything else with 401. Treat the token as a secret; rotate it via LoyaltyPlant. Example: "Bearer sk_live_abc123".

Responses

Response samples

Content type
application/json
{
  • "capabilities": [
    ]
}

Health Check

Returns the health status of the integration service. Payments-service polls this endpoint periodically to decide whether to route traffic to your integration. Return DOWN (or 503) when your PSP connection is unavailable so payments-service can stop sending you live payments until you recover.

header Parameters
Authorization
required
string

Bearer token authenticating payments-service to your integration (the outbound token LoyaltyPlant provisions for you during onboarding). Validate it on every request (constant-time compare) and reject anything else with 401. Treat the token as a secret; rotate it via LoyaltyPlant. Example: "Bearer sk_live_abc123".

Responses

Response samples

Content type
application/json
{
  • "status": "UP"
}