Stored receipt download

Retrieve an existing receipt file using the supplied hash-bearing link. This is distinct from invoice rendering and receipt-generation acknowledgement.

TaskOperation
Download an available receipt fileReceipt download

Download a stored receipt

GET /api/v1/transaction/receipt/download

Reads an existing active receipt from private storage and streams its bytes. On successful streaming it increments the receipt's recorded count_downloads and saves. Repeating this GET can increment the download count again. The file is not generated by this request.

Before you call

Obtain the selected receipt.download_link from transaction detail when the record is available. Use its supplied user/file values; a local transaction ID, email or member token is not either hash. Possession of the correct pair authorizes this guest-granted download. Keep the link within the intended recipient flow; no expiry or maximum-download limit is specified here.

For Business Info and account/email template configuration, see the operator receipt setup instructions. A template should offer a receipt link only when receipt.download_link is available. Configuring the template does not create the stored file or make every transaction downloadable.

The action finds receipt by file hash, checks active status, verifies the user hash against receipt/user/resource/transaction association and checks the current transaction file hash. It has no require-user/resource guard and needs no member headers in these examples. Provider payment status is not checked by this download.

Request

GET query parameters, no body/Content-Type. Header member/resource credentials are not its required download credential.

NameLocationTypeRequirement / constraintMeaning
userquerystringrequired, max128 before sanitizationSupplied receipt user hash, not numeric user ID. Trim/string/strip-tags sanitization follows validation.
filequerystringrequired, max128 before sanitizationSupplied receipt file hash, not filename/path or transaction ID.

Result

Direct file bytes from private storage; no redirect or JSON success wrapper. Content-Type is the storage object's ContentType, Content-Disposition is attachment with the receipt original_name, and Pragma is public. The handler sets no explicit success status. Receipts generated by the documented helper contain PDF bytes, but its storage uploader sets ContentType to the filename extension pdf, not application/pdf. The download forwards the stored value; do not require a standard PDF MIME label to recognize this documented branch. The post-stream count save has no checked success boolean, so returned bytes do not guarantee that the counter persisted.

Use sample runtimes. RECEIPT_USER_HASH and RECEIPT_FILE_HASH are placeholders for the supplied link's values; no real receipt or hash was used. WALLKIT_API_BASE remains scheme/host only. The three requests encode the same two query parameters.

cURL

curl "${WALLKIT_API_BASE}/api/v1/transaction/receipt/download" \
  --get \
  --data-urlencode "user=${RECEIPT_USER_HASH}" \
  --data-urlencode "file=${RECEIPT_FILE_HASH}" \
  --dump-header download-headers.txt \
  --output download-body.bin

Inspect download-headers.txt and HTTP status before treating download-body.bin as a PDF; the request can return JSON errors. These are illustrative local output filenames, not files created during authoring.

JavaScript

// Node.js 18+; built-in fetch. Separate binary from JSON errors.
async function main() {
  const url = new URL("/api/v1/transaction/receipt/download", process.env.WALLKIT_API_BASE);
  url.searchParams.set("user", process.env.RECEIPT_USER_HASH);
  url.searchParams.set("file", process.env.RECEIPT_FILE_HASH);
  const response = await fetch(url, { method: "GET", headers: {} });
  const mediaType = response.headers.get("content-type") || "";
  if (mediaType.includes("application/json")) {
    console.log(response.status, await response.json());
  } else if (!response.ok) {
    console.log(response.status, await response.text());
  } else {
    const bytes = await response.arrayBuffer();
    console.log(response.status, mediaType, bytes.byteLength);
  }
}
main().catch(console.error);

Python

# Python 3; standard library only.
import json
import os
from urllib.error import HTTPError
from urllib.parse import urlencode, urljoin
from urllib.request import Request, urlopen

url = urljoin(os.environ["WALLKIT_API_BASE"], "/api/v1/transaction/receipt/download") + "?" + urlencode({"user": os.environ["RECEIPT_USER_HASH"], "file": os.environ["RECEIPT_FILE_HASH"]})
request = Request(url, headers={}, method="GET")
try:
    with urlopen(request) as response:
        media_type = response.headers.get("Content-Type", "")
        if "application/json" in media_type:
            print(response.status, json.load(response))
        else:
            print(response.status, media_type, len(response.read()))
except HTTPError as error:
    media_type = error.headers.get("Content-Type", "")
    if "application/json" in media_type:
        print(error.code, json.load(error))
    else:
        print(error.code, error.read().decode("utf-8", errors="replace"))

Successful response description: stored PDF receipt bytes with forwarded storage Content-Type (the documented uploader supplies pdf), with attachment filename from original_name. JSON primary response is N/A because this operation streams the file. Treat the counter change as activity, not proof of financial completion or a new access grant.

Consequential alternate: inactive receipt

HTTP 409:

{"error":"invalid_data","error_description":"File is not available","req_guid":"example-request"}

The receipt record exists but is inactive. Use current transaction detail/receipt availability through the intended integration; a member token does not override inactive status.

Recovery

HTTPCode / responseCauseNext action
409invalid_data with validation descriptionMissing/oversized user/file query value.Preserve the supplied link values and URL-encode them; do not substitute account IDs.
409invalid_data, Invalid dataMissing receipt, hash mismatch, missing transaction or caught storage lookup failure.Check current supplied receipt link and availability with the integration owner; do not guess hashes.
409invalid_data, File is not availableInactive receipt.Use the intended current receipt record/availability flow.

The broad catch forwards the caught exception code; non-validator failures can lack a stable HTTP status even though the JSON error code is invalid_data. Do not assume every failure is a guaranteed409 or expired-link result. Storage failures and missing records intentionally share generic descriptions.

Next task

Present the downloaded file in your application after checking its media/status. For a missing receipt, read transaction detail and use the authorized generation request where appropriate; no blind download/generation replay is promised safe.

Full diagram

Use the arrow keys to scroll. Escape closes this view.

Search documentation

Enter at least 2 characters.

    ↑ ↓ move through results · Enter opens · Escape closes