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.
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.
| 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). |
| 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) |
{- "paymentId": "8e2b7d6a-1f3c-4e9a-9c2b-77a0e3d4f111",
- "transactionReference": "psp_txn_7Hagain2",
- "amount": 49.99,
- "settings": {
- "currency": "USD"
}
}{- "status": "SUCCESS",
- "transactionReference": "psp_txn_7HagaIn2"
}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. returnUrls
behaves the same way too: it arrives on every call, and your page sends
the browser to one of the three addresses once it is done.
| 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). |
| 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: | |||||||||||||
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 Which field carries the payload depends on
payments-service only sends an instrument whose capability you declared, so you never have to handle a type you did not opt into. Both | |||||||||||||
object (ReturnUrls) Where your page sends the browser once the customer is done with it — a hosted checkout, a hosted card-entry form, or a 3-D Secure challenge. These are navigation only. payments-service resolves the outcome from your callback, never from the address the browser lands on. Never append a card token or any PSP secret to them. Send the browser to exactly one of them, and send it every time. The mobile app closes its payment screen on this navigation and on nothing else: it does not poll. A page that never redirects strands the customer on itself, and a screen the customer then closes by hand counts as an abandoned payment even when the money moved. Pick the address that matches the outcome you report in the callback —
the customer is shown what the address says. Redirect only once your PSP
has given you that answer: a customer arriving back from the bank is not
one. If the answer is still not final when your session runs out, use
You may append your own query string; matching is on the prefix. Do not change the path. |
{- "paymentId": "8e2b7d6a-1f3c-4e9a-9c2b-77a0e3d4f111",
- "amount": 49.99,
- "currency": "USD",
- "settings": {
- "currency": "USD",
- "merchantId": "acct_3092f1"
}, - "returnUrls": {
}
}{- "status": "SUCCESS",
- "transactionReference": "psp_txn_7HagaIn2"
}{- "status": "SUCCESS",
- "transactionReference": "psp_txn_7HagaIn2"
}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.
| 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). |
| 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) |
{- "paymentId": "8e2b7d6a-1f3c-4e9a-9c2b-77a0e3d4f111",
- "transactionReference": "psp_txn_7Hagain2",
- "amount": 10,
- "reason": "Item out of stock",
- "settings": {
- "currency": "USD"
}
}{- "status": "SUCCESS",
- "transactionReference": "psp_txn_7HagaIn2"
}Void an authorization before it is captured (releases the hold).
transactionReference is the value returned from authorize.
Idempotent on Idempotency-Key.
| 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). |
| 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) |
{- "paymentId": "8e2b7d6a-1f3c-4e9a-9c2b-77a0e3d4f111",
- "transactionReference": "psp_txn_7Hagain2",
- "settings": {
- "currency": "USD"
}
}{- "status": "SUCCESS",
- "transactionReference": "psp_txn_7HagaIn2"
}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.
| 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". |
| 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) |
{- "transactionReference": "psp_txn_7Hain2",
- "paymentId": "8e2b7d6a-1f3c-4e9a-9c2b-77a0e3d4f111",
- "settings": {
- "currency": "USD",
- "merchantId": "acct_3092f1"
}
}{- "status": "CAPTURED",
- "transactionReference": "psp_txn_7HagaIn2",
- "rawStatus": "captured"
}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.
| 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). |
| enrollmentId required | string <uuid> Enrollment identifier minted by payments-service. Echo it back in
the |
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 | |
object (ReturnUrls) Where your page sends the browser once the customer is done with it — a hosted checkout, a hosted card-entry form, or a 3-D Secure challenge. These are navigation only. payments-service resolves the outcome from your callback, never from the address the browser lands on. Never append a card token or any PSP secret to them. Send the browser to exactly one of them, and send it every time. The mobile app closes its payment screen on this navigation and on nothing else: it does not poll. A page that never redirects strands the customer on itself, and a screen the customer then closes by hand counts as an abandoned payment even when the money moved. Pick the address that matches the outcome you report in the callback —
the customer is shown what the address says. Redirect only once your PSP
has given you that answer: a customer arriving back from the bank is not
one. If the answer is still not final when your session runs out, use
You may append your own query string; matching is on the prefix. Do not change the path. | |
object Partner/outlet-specific configuration provisioned by LoyaltyPlant
(NOT chosen by the integrator). Common keys: |
{- "enrollmentId": "3f6c1b90-2d4e-4a11-9f77-0b2c5d8e4a10",
- "customer": {
- "email": "customer@example.com",
- "name": "Jane Doe",
- "language": "en"
}, - "returnUrls": {
}, - "settings": {
- "currency": "USD",
- "merchantId": "acct_3092f1"
}
}{- "status": "PENDING",
- "transactionReference": "psp_enr_9KdlaQ7",
- "expectedTtlSeconds": 900
}{- "enrollmentId": "3f6c1b90-2d4e-4a11-9f77-0b2c5d8e4a10",
- "transactionReference": "psp_enr_9KdlaQ7",
- "status": "SUCCESS",
- "card": {
- "token": "tok_4gld6myqrxbu5g3gcxlqcwbxfa",
- "maskedPan": "**** 4242",
- "brand": "VISA",
- "expiryMonth": 12,
- "expiryYear": 2029
}
}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.
| 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". |
| enrollmentId required | string <uuid> Enrollment identifier from the original |
| 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": "3f6c1b90-2d4e-4a11-9f77-0b2c5d8e4a10",
- "transactionReference": "psp_enr_9KdlaQ7"
}{- "status": "COMPLETED",
- "transactionReference": "psp_enr_9KdlaQ7",
- "card": {
- "token": "tok_4gld6myqrxbu5g3gcxlqcwbxfa",
- "maskedPan": "**** 4242",
- "brand": "VISA",
- "expiryMonth": 12,
- "expiryYear": 2029
}, - "rawStatus": "card_verified"
}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.
| 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". |
{- "capabilities": [
- "CAPTURE",
- "DIRECT_REFUND",
- "REDIRECT_FLOW",
- "DIRECT_WEBHOOK",
- "STATUS_POLLING"
]
}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.
| 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". |
{- "status": "UP"
}