Skip to content

Developers / Integration guides

Payment integration guides

Set up a test integration, connect checkout, verify payment status and handle webhooks, refunds and retries.

HTTP API v1Contract 0.1.0-local
Local/test runtime reference

The merchant integration uses Alzaf Pay keys, IDs, normalized states and signed events. Live payment processing and hosted availability are not established by this reference. No request is executed from these pages.

Getting started

Build your first payment flow

Start with a synthetic local order. Follow the same payment through checkout, verification and signed events.

  1. Calculate the order amount on your server in integer BDT poisha. Store the reference, immutable request and operation key before network I/O.

  2. Create an intent from your server and save data.id with your order. Creation alone does not charge.

  3. Redirect the authenticated customer to the returned checkout_url. Treat its capability as private; never construct one from a payment ID.

  4. Complete the supported local test flow. Retrieve canonical state on your server and match ID, reference, amount, currency and scope.

  5. Fulfill once after verified success using a transactional uniqueness guard. A browser return or HTTP 202 never authorizes fulfillment.

  6. Verify and durably deduplicate signed events. Test refund recovery, response loss and cross-merchant/environment isolation before any approved live pilot.

Run the Laravel API and Vue frontend as separate application projects using their pinned setup instructions. Local/test execution is the verified starting point; hosted sandbox and live processing need separate acceptance.

Use your assigned merchant workspace, then create or select an application project. Obtain your private local account from the setup owner; public merchant onboarding is not implemented. Create a test secret key with the minimum scopes when your role permits it. Package eligibility and current role permissions remain separate concerns.

Core concepts

Authenticate from your server

Project-bound bearer keys determine merchant, project and environment. The browser never receives a merchant secret.

Send Authorization: Bearer $ALZAF_SECRET_KEY from your backend. Test secret keys use alz_test_sk_; Live uses a separate key context. Store credentials privately, never in frontend variables, URLs, support messages or source control.

Required endpoint scopes come from the runtime contract. Stored key scopes are intersected with the creator’s current merchant role and project access. Revoked, expired or out-of-scope keys do not grant access.

Publishable keys cannot authorize financial APIs. Checkout capabilities are restricted to their own payment surface and cannot read general merchant data or request refunds.

Server request headers
Authorization: Bearer <SERVER_ONLY_TEST_SECRET>
Accept: application/json
Content-Type: application/json
Idempotency-Key: <PERSISTED_OPERATION_KEY>

Core concepts

Keep Sandbox and Live separate

A selected Live workspace is a data context, not processing approval.

The local API base is http://127.0.0.1:8000/v1. Hosted URLs in the target specification do not prove provisioning. Supply an operator-approved base URL to your server when another environment has been accepted.

Use distinct test/live keys, credentials, webhook secrets and data scope. Nothing automatically promotes from Sandbox. Browser X-Alzaf-Environment is an internal session selector, not a public bearer-key scope override.

Current Live activation gates reject financial processing. Controlled live use requires eligible accounts, commercial arrangements, applicable legal/compliance review, credentials, security/operations and actual end-to-end acceptance. Provider consent or licence acquisition alone does not complete every requirement.

Current money remains BDT-only. Do not infer international or multicurrency acceptance from an underlying processor name.

Payment lifecycle

Model one order and one payment

Use Alzaf Pay identifiers and normalized state regardless of internal processing configuration.

Amounts are positive JSON integers in BDT poisha: 10050 represents BDT 100.50. The target safe integer ceiling is 9007199254740991; configured business and payment-service limits can be lower. Do not send floats or trust browser-calculated totals.

CREATED, PENDING and PROCESSING are not paid. SUCCEEDED permits fulfillment only after server-side order matching. PARTIALLY_REFUNDED and REFUNDED represent capture followed by refund activity. FAILED, CANCELLED and EXPIRED do not authorize fulfillment.

An unknown outcome remains unresolved. Fetch canonical state and recover the original operation; never issue another charge, cancel unresolved work or switch processing routes merely because a request timed out.

Payment lifecycle

Redirect to the returned checkout URL

Hosted checkout is a restricted capability. A redirect result is never financial authority.

Create on your server, retain data.id and send only the returned checkout_url to its authenticated customer. A pay_ identifier alone is not a checkout credential. Do not log the URL fragment or embed merchant secrets in the page.

The checkout surface verifies server state. On return, show a checking/pending state until your backend retrieves and matches the canonical payment. Do not fulfill from query parameters or a success-looking browser message.

The local execution slice supports simulated outcomes. Real method eligibility requires accepted account capabilities and routes; external authentication or hosted checkout may display mandatory payment-service branding or disclosures.

Payment lifecycle

Request and reconcile refunds

Submit a gross integer amount with a durable refund operation key, then inspect the result.

Send payment_id, a positive integer amount and an optional reason. For a full refund use the remaining eligible captured amount, not an omitted amount. Give each legitimate partial refund its own persisted operation and immutable payload.

HTTP 202 means accepted. Current local mock refunds verify synchronously, but real-provider asynchronous execution is not accepted. A timeout does not release a reservation or permit an overlapping replacement.

Aggregate refunds cannot exceed captured gross. Quoted mock captures require their retained explicit refund policy. Internal fee allocations are not public request inputs.

Events

Receive and verify signed events

Authenticate exact bytes, commit a durable receipt, then apply effects once.

Register an endpoint with a nonempty explicit events list. Save its signing secret once in server-side secret storage. Listing does not recover the secret. Registration is not response-replayed; after uncertainty inspect the endpoint list before trying again.

Verify HMAC-SHA256 over timestamp + a literal period + the exact raw body. Compare in constant time and reject timestamps more than 300 seconds in the past or future. Match the signed event ID and expected merchant/project/environment.

Deduplicate by project, environment and event ID with a retained payload hash. Commit receipt before acknowledging 2xx, then process asynchronously. A new delivery ID is not a new business event.

Delivery is at least once and may be out of order. Read canonical state for conflicting snapshots, preserve fulfillment uniqueness and reconcile refunds separately. Public HTTPS networking and operational signing-secret recovery/rotation remain acceptance gates.

Webhook signature recipe
Alzaf-Signature: t=<unix-seconds>,v1=<lowercase-hex>
Alzaf-Event-Id: evt_<event-id>
Alzaf-Delivery-Id: del_<delivery-id>

signed_message = timestamp + "." + exact_raw_body
signature = HMAC-SHA256(endpoint_signing_secret, signed_message)

Reliability

Diagnose errors without leaking data

Use machine codes and a safe request reference. An HTTP failure is not a confirmed payment decline.

Read error.code, the safe message and request_id. X-Request-Id matches the response envelope, except that an idempotent replay retains the original operation response/reference. Do not parse message text as business logic.

Correct authentication, permission or validation failures before retrying. Honor Retry-After when a 429 rate-limit response supplies it. A transport timeout, lost/malformed response or transient server error can leave the financial outcome unknown.

Preserve the original key/body, retrieve state and escalate using safe payment/refund/event/request IDs. Do not log credentials, checkout capabilities, customer payment details or raw internal processing payloads.

Reliability

Persist the operation before sending it

Financial create, confirm, cancel and refund requests require an original key and immutable payload.

Idempotency-Key is 16–128 ASCII letters, digits or . _ : -. Store it with the business operation before the request. Use a different key for a genuinely different operation, never as a timeout workaround.

Same key and canonical validated payload replay the committed status/body. Changed payload returns 409 IDEMPOTENCY_CONFLICT. Concurrent requests serialize on the server; if your response is lost, recover the original operation without creating a parallel charge.

Scope includes merchant/project/environment, HTTP method and normalized path; API key rotation does not change it. Keep operation keys with business records permanently. The current runtime makes no response-retention expiry guarantee. Prolonged uncertainty never authorizes a replacement operation.

Reliability

Continue with the returned cursor

Transaction and webhook endpoint lists use opaque, scoped, expiring cursors.

Use limit 1–100, default 20. Continue with page.next_cursor, retaining the same limit and query context. Read data, page.has_more and request_id; stop when next_cursor is null.

Cursors expire 15 minutes after the initial traversal without extension. Invalid, expired or changed-scope cursors return 422. Start a fresh traversal after expiry; never decode or construct cursor internals.

This is not a frozen export snapshot. New inserts can require a fresh traversal. Follow each resource’s documented ordering and deduplicate records in your application when needed.

Compatibility

Separate API version from availability

A versioned schema describes a contract; acceptance establishes which behavior is available.

Public routes use /v1 and event envelopes declare api_version: v1. Runtime reference and target OpenAPI are distinct: the runtime reference describes implemented local routes, while the target includes planned operations/options.

Preserve Alzaf identifiers, normalized states and event/signature semantics across internal processing changes. Ignore unrecognized optional fields and durably record unknown event types without inventing effects.

Breaking authentication, field meaning, required-field or state changes need a reviewed migration/version contract. Hosted deprecation policy is a launch commitment, not evidence of an operating live service. Settlement execution and payouts remain outside current acceptance.