> ## Documentation Index
> Fetch the complete documentation index at: https://docs.avraapi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# PayPlus

> Use PayPlus hosted redirect checkout with signed callback verification and status reconciliation.

## When to use PayPlus

Use PayPlus for a hosted redirect checkout in LKR or USD. The shared `createOrder()` function creates the provider session from your backend and returns a public redirect URL. A signed PayPlus notification is the payment evidence; your backend preserves its exact body and authorization header before requesting shared completion.

| Capability | Support |
| - | - |
| Checkout mode | `redirect` only |
| Currencies | LKR and USD |
| PHP SDK | Status lookup and signed callback payload wrapper released |
| Laravel SDK | Released through the `AvraAPI::payment()` Facade accessor |
| Node.js SDK | Released: status reconciliation and signed callback payload wrapper. |
| Payment Elements | Standard PayPlus redirect hand-off |
| Completion rule | Signed callback, with default provider-status reconciliation for a successful callback |

<Note>
  The shared UPG lifecycle—availability, `createOrder()`, and `completePayment()`—is documented in [Quick Setup](/universal-payment-gateway/quick-setup). This page covers only the released PayPlus standard hosted checkout.
</Note>

## SDK Functions

<Tabs>
  <Tab title="PHP SDK">
    | Function | Purpose |
    | - | - |
    | `payment()->payplus()->status()` | Retrieves the safe provider status summary for a known PayPlus order. |
    | `new PayPlusCallbackPayload(...)->toArray()` | Preserves the exact signed callback body and Authorization header for completion. |
    | `payment()->completePayment(...)` | Verifies the signed callback and produces the normalized payment result. |
  </Tab>

  <Tab title="Laravel SDK">
    | Function | Purpose |
    | - | - |
    | Laravel Facade | Use `AvraAPI::payment()` with the released typed helpers shown in the Laravel samples below. |
  </Tab>

  <Tab title="Node.js SDK">
    | Function | Purpose |
    | - | - |
    | `client.payment().payplus().status()` | Retrieves the server-side PayPlus status for one order. |
    | `new PayPlusCallbackPayload(...).toPayload()` | Preserves exact signed callback evidence. |
    | `client.payment().completePayment(...)` | Verifies and completes the stored payment session. |
  </Tab>
</Tabs>

## Retrieve a PayPlus payment status

Use `status()` from your backend when you need a read-only observation for a pending order or operational diagnosis. It queries the configured PayPlus status endpoint for the stored order ID and maps only its safe summary. It is not browser-side payment proof.

<Tabs>
  <Tab title="PHP SDK">
    ```php theme={null}
    use Avraapi\Apix\Payments\GatewayEnvironment;

    $status = $apix->payment()->payplus()->status(
        orderId: $order->id,
        gatewayEnvironment: GatewayEnvironment::Sandbox,
    );
    ```

    **SDK response** — `PayPlusStatus`:

    ```json theme={null}
    {
      "order_id": "ORDER-2026-000184",
      "provider_status": "SUCCESS",
      "timestamp": "2026-09-28T10:30:00+00:00",
      "request_id": "01j..."
    }
    ```

    The SDK exposes `$status->orderId`, `$status->providerStatus`, `$status->timestamp`, and `$status->requestId`. `timestamp` is the provider value when supplied; the current status contract represents an absent value as an empty string. This lookup intentionally omits raw provider data, session tokens, payment rails, and customer/card details.
  </Tab>

  <Tab title="Laravel SDK">
    ```php theme={null}
    use Avraapi\Laravel\Facades\AvraAPI;

    use Avraapi\Apix\Payments\GatewayEnvironment;

    $status = AvraAPI::payment()->payplus()->status(
        orderId: $order->id,
        gatewayEnvironment: GatewayEnvironment::Sandbox,
    );
    ```
  </Tab>

  <Tab title="Node.js SDK">
    ```ts theme={null}
    import { GatewayEnvironment } from '@avraapi/node-sdk';

    const status = await client.payment().payplus().status(
      order.id,
      GatewayEnvironment.Sandbox,
    );
    ```
  </Tab>
</Tabs>

## Preserve a signed PayPlus callback

PayPlus signs the exact callback body. Read the original body and Authorization header on your backend before any JSON, Base64, or string transformation. The wrapper does not parse or verify the callback itself; it preserves the evidence for shared completion.

<Tabs>
  <Tab title="PHP SDK">
    ```php theme={null}
    use Avraapi\Apix\Payments\PayPlus\PayPlusCallbackPayload;

    $payload = new PayPlusCallbackPayload(
        rawBody: (string) file_get_contents('php://input'),
        authorization: (string) ($_SERVER['HTTP_AUTHORIZATION'] ?? ''),
    );
    ```

    **SDK response** — `PayPlusCallbackPayload::toArray()`:

    ```json theme={null}
    {
      "raw_body": "eyJvcmRlcklkIjoiT1JERVItMjAyNi0wMDAxODQifQ==",
      "authorization": "hmac provider-signature"
    }
    ```

    Keep both strings server-only. The raw callback body and signature can be returned only through explicitly requested, protected diagnostic response modes; they do not belong in browser state, logs, queues, or analytics.
  </Tab>

  <Tab title="Laravel SDK">
    ```php theme={null}
    use Avraapi\Apix\Payments\PayPlus\PayPlusCallbackPayload;

    $payload = new PayPlusCallbackPayload(
        rawBody: (string) file_get_contents('php://input'),
        authorization: (string) ($_SERVER['HTTP_AUTHORIZATION'] ?? ''),
    );
    ```
  </Tab>

  <Tab title="Node.js SDK">
    ```ts theme={null}
    import { PayPlusCallbackPayload } from '@avraapi/node-sdk';

    const rawBody = await readRawRequestBody(request); // Your server helper.
    const payload = new PayPlusCallbackPayload(
      rawBody,
      String(request.headers.authorization ?? ''),
    ).toPayload();
    ```
  </Tab>
</Tabs>

## Complete a verified PayPlus callback

Pass the stored completion context and the payload wrapper to the shared completion API. By default, a verified successful callback is also reconciled through the PayPlus status endpoint. The status observation can confirm, conflict with, or be temporarily unavailable relative to the signed callback.

<Tabs>
  <Tab title="PHP SDK">
    ```php theme={null}
    use Avraapi\Apix\Payments\GatewayCode;
    use Avraapi\Apix\Payments\PaymentCompletionOptions;

    $result = $apix->payment()->completePayment(new PaymentCompletionOptions(
        gateway: GatewayCode::PayPlus,
        completionContext: $order->completion_context,
        payload: $payload->toArray(),
    ));
    ```

    **SDK response** — `PaymentCompletionResult`:

    ```json theme={null}
    {
      "gateway": "payplus",
      "verified": true,
      "payment_status": "succeeded",
      "provider_status": "SUCCESS",
      "order_id": "ORDER-2026-000184",
      "gateway_reference": "PP-320000000000",
      "amount": "12000.00",
      "currency": "LKR",
      "reconciliation": {
        "attempted": true,
        "state": "matched",
        "authority": "provider_status",
        "callback_status": "SUCCESS",
        "secondary_status": "SUCCESS",
        "retry_recommended": false
      }
    }
    ```

    Use `$result->paymentStatus`, `$result->verified`, and `$result->reconciliation` to decide your order state. A status lookup outage makes a formerly successful callback `unknown` with `retry_recommended: true`; do not fulfil until a later backend retry returns a verified `succeeded` result.
  </Tab>

  <Tab title="Laravel SDK">
    ```php theme={null}
    use Avraapi\Laravel\Facades\AvraAPI;

    use Avraapi\Apix\Payments\GatewayCode;
    use Avraapi\Apix\Payments\PaymentCompletionOptions;

    $result = AvraAPI::payment()->completePayment(new PaymentCompletionOptions(
        gateway: GatewayCode::PayPlus,
        completionContext: $order->completion_context,
        payload: $payload->toArray(),
    ));
    ```
  </Tab>

  <Tab title="Node.js SDK">
    ```ts theme={null}
    import { GatewayCode, PaymentCompletionOptions } from '@avraapi/node-sdk';

    const result = await client.payment().completePayment(
      new PaymentCompletionOptions({
        gateway: GatewayCode.PayPlus,
        completionContext: order.completionContext,
        payload,
        reconcileProvider: true,
      }),
    );
    ```
  </Tab>
</Tabs>

## Payment Elements

PayPlus presents the payment rails enabled for the merchant account on its hosted page. Payment Elements should render one redirect card and must not advertise individual cards, wallets, QR rails, or other provider features that your PayPlus profile may not enable.

```js theme={null}
elements.renderMethods('#payment-methods', {
  methods: [{
    gateway: 'payplus',
    mode: 'redirect',
    label: 'Pay with PayPlus',
    description: 'Continue to PayPlus to choose an available payment method securely.',
  }],
});
```

Elements redirects only to the public provider URL prepared by your backend. For rendering, visual customization, events, and the checkout lifecycle, see [More Payment Elements features](/universal-payment-gateway/payment-elements/overview).

## Gateway-specific options

The released standard checkout exposes only these backend-controlled `providerOptions` during order creation:

* `is_nic_editable` — boolean; defaults to `true`.
* `plugin_version` — optional identifier, defaulting to `1.0.0`.
* `source` — optional source label, defaulting to `AVRAAPI`.
* Customer information and configured callback/return URLs are validated by the backend; credentials, merchant secret, hosted-session token, and buyer-controlled status values are never options.

LankaQR, reusable card tokens, recurring charging, JustPay token flows, and PayPlus-specific rails are not part of this released standard checkout. Do not treat the provider's wider product API as an enabled AvraAPI SDK capability.

## Completion, webhooks, and safety

The notification callback is authoritative evidence only after HMAC verification, Base64 decoding, and binding its order ID, amount, and currency to the stored completion context. A browser return merely returns the customer to your application; it does not complete payment.

Keep the default `reconcileProvider: true` for successful callbacks. A final result can be `succeeded`, `pending`, `failed`, `cancelled`, or `unknown`; fulfil only a verified `succeeded` result. Handle a non-final result with bounded, idempotent backend retry rather than showing a provider status as success.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.