PAYTAPPS Simple digital payments for customers and growing businesses.
Merchant API v1

PAYTAPPS developer platform

Create hosted checkout sessions, reconcile payment status, issue refunds, and receive signed events with server-side integrations built for retry safety.

Canonical HMAC Idempotent writes Signed webhooks
POST /api/v1/checkout
X-PayOffice-Signature-Version: v2
Idempotency-Key: order_1048_attempt_1

{
  "amount": "49.99",
  "currency": "USD",
  "merchant_reference": "ORDER-1048",
  "customer": {
    "email": "buyer@example.com"
  }
}
201 Createdcheckout_url ready for redirect
Quickstart Authentication Integrations API reference Webhooks Errors
01

From account to first payment

Use test mode until the entire redirect, webhook, and reconciliation path succeeds.

Create merchant profile

Open the Merchant workspace, enter the business identity and webhook URL, then submit for review.

Complete approval

An administrator activates test access. Live mode additionally requires approved owner KYC.

Issue a scoped key

Create a separate expiring key for each website, app, or point-of-sale integration.

Create checkout

Send an idempotent signed request and redirect the customer to the returned checkout URL.

Confirm server-side

Use the completed webhook or retrieve the payment before marking the local order paid.

Keep merchant secrets on the server.

Never sign requests in browser JavaScript, mobile bundles, page source, or public WordPress settings output.

Open API keys
02

Canonical request authentication

Version 2 binds the timestamp, HTTP method, exact request target, and raw body digest into one HMAC.

Required headers

X-PayOffice-Key
The scoped public key shown in the merchant workspace.
X-PayOffice-Timestamp
Current Unix timestamp. Requests outside the five-minute tolerance are rejected.
X-PayOffice-Signature-Version
Send v2. Version 1 remains available only for legacy compatibility.
X-PayOffice-Signature
Lowercase HMAC-SHA256 hex digest of the canonical value.
Idempotency-Key
Required by the integration guides for checkout and refund writes.
Canonical value
{unix_timestamp}\n{UPPERCASE_METHOD}\n{request_path_with_query}\n{sha256_hex_of_raw_body}
PHP signer
$body = json_encode($payload, JSON_UNESCAPED_SLASHES);
$timestamp = (string) time();
$target = '/api/v1/checkout';
$canonical = implode("\n", [
    $timestamp,
    'POST',
    $target,
    hash('sha256', $body),
]);
$signature = hash_hmac('sha256', $canonical, $secretKey);
03

Choose an integration

Use the packaged clients or implement the same OpenAPI and signing contract from any server framework.

Laravel client package

The download contains a service provider, configuration file, typed client methods, canonical signing, request retries, and webhook verification.

  1. Extract the package into packages/payoffice/laravel-client.
  2. Add a Composer path repository and require payoffice/laravel-client.
  3. Publish config and add the API URL, public key, secret, and webhook secret to .env.
  4. Create checkout server-side, then redirect to checkout_url.
Download Laravel client
Create checkout
use PayOffice\Laravel\PayOfficeClient;

$checkout = app(PayOfficeClient::class)->createCheckout([
    'amount' => number_format($order->total, 2, '.', ''),
    'currency' => $order->currency,
    'merchant_reference' => $order->number,
    'client_reference_id' => 'order_'.$order->id,
    'description' => 'Order '.$order->number,
    'customer' => [
        'name' => $order->customer_name,
        'email' => $order->customer_email,
    ],
    'return_url' => route('orders.paid', $order),
    'cancel_url' => route('orders.show', $order),
], 'order_'.$order->id);

return redirect()->away($checkout['checkout_url']);
04

Merchant API reference

Base URL https://paytapps.com/api/v1. Every response includes X-PayOffice-Request-ID.

GET /merchant

Read merchant status, environment, webhook configuration, and account health.

merchant:read
POST /checkout

Create an idempotent hosted checkout with customer and line-item context.

checkout:create
GET /payments

List and filter merchant payments with pagination.

payments:read
GET /payments/{checkout_token}

Read status, receipt, refund history, and order details.

payments:read
POST /payments/{checkout_token}/cancel

Cancel a pending checkout session.

checkout:cancel
POST /payments/{checkout_token}/refund

Create a full or partial idempotent refund.

refunds:create
GET /refunds/{refund_id}

Retrieve a refund and its wallet transaction reference.

refunds:read
GET /webhooks

Inspect webhook payloads, attempts, responses, and delivery state.

webhooks:read
POST /webhooks/test

Queue a signed test event to the configured endpoint.

webhooks:test
POST /webhooks/{event_id}/retry

Retry a failed, queued, or skipped webhook event.

webhooks:retry
OpenAPI specification Import into Postman, Insomnia, Bruno, Swagger UI, or an API client generator.
05

Signed webhook delivery

Respond with any 2xx status quickly, deduplicate by webhook ID, and move slow business logic to a queue.

Available events

merchant.payment.completedCheckout payment completed
merchant.payment.cancelledCheckout payment cancelled
merchant.payment.expiredCheckout payment expired
merchant.payment.refundedCheckout payment refunded
merchant.payment_link.completedPayment link completed
Event IDX-PayOffice-Webhook-ID Event typeX-PayOffice-Webhook-Event AttemptX-PayOffice-Webhook-Attempt SignatureX-PayOffice-Webhook-Signature

Financial settlement fields: completed payment payloads separate payer_fee, merchant-owned processing_fee, and wallet net_amount. Use the net amount for merchant reconciliation.

Verify raw webhook body
$body = file_get_contents('php://input');
$header = $_SERVER['HTTP_X_PAYOFFICE_WEBHOOK_SIGNATURE'] ?? '';

if (! preg_match('/^t=(\d+),v1=([a-f0-9]{64})$/', $header, $match)) {
    http_response_code(400);
    exit;
}

[$header, $timestamp, $signature] = $match;
if (abs(time() - (int) $timestamp) > 300) {
    http_response_code(400);
    exit;
}

$expected = hash_hmac(
    'sha256',
    $timestamp.'.'.$body,
    getenv('PAYOFFICE_WEBHOOK_SECRET')
);

if (! hash_equals($expected, $signature)) {
    http_response_code(401);
    exit;
}

http_response_code(204);
06

Errors and recovery

Use the HTTP status, response message, field errors, and request ID together when investigating an integration.

200 / 201Success

Read the returned object. A repeated idempotent write can return 200 with idempotent_replay: true.

404Not found

The resource does not exist or belongs to a different merchant.

409Conflict

An idempotency key was reused with different data, or a delivered webhook was retried.

422Invalid request

Authentication, scope, account state, or field validation failed. Inspect the errors object.

429Rate limited

Back off with jitter and retry. Do not change the idempotency key for the same write.

5xxPlatform error

Retry idempotently and keep the request ID for support and API request-log correlation.

Integration support

Include the request ID, endpoint, timestamp, and merchant public-key prefix. Never send a secret key.

Contact support