Skip to content
DLTdevelopers
Sign in
DLT Merchant Portal/Collections Public documentation

Metrics & charts

For DLT Merchant Portal developers · DLT maintainers · Contract v1.7

Configured API origin: https://stage-checkout.dxp.dtic.com.ph

Build with the right credentials. API access requires the documented authentication and permissions. Examples use synthetic data and placeholder origins.

10. Collection metrics and chart data

10.1 Summary/chart endpoints and filters

Method and path Access Status and result
GET /portal/collections/metrics Active Administrator or Developer, user PAT with merchant-portal scope 200, gross collection totals, counts, and success rate
GET /portal/collections/charts Active Administrator or Developer, user PAT with merchant-portal scope ordered daily/monthly/yearly buckets and reconciled overall totals

Summary and charts share these population filters:

Query Contract
environment test or live
from, to Required valid ISO 8601 instants including seconds and explicit Z/offset; up to six fractional-second digits; start-inclusive/end-exclusive; from < to
currency Required supported three-letter uppercase currency from the local catalogue; no mixing currencies
timezone Optional IANA timezone; default Asia/Manila; numeric offsets are rejected
payment_brand Optional nonempty string, maximum 100 characters; equality filter matching the transaction list contract
interval Required for charts: day, month, or year; rejected by summary endpoint

Both endpoints echo the effective timezone and validated input range. Filtering compares explicit from/to instants after converting them to the application's database timestamp timezone (config('app.timezone')); changing the reporting timezone does not reinterpret the same instants. The ten-calendar-year range cap is measured from from in the reporting timezone, clipping a leap-day anniversary to the last valid calendar day. Named reporting timezones and defaults are independent of the workstation timezone. Chart boundaries follow the reporting timezone, including daylight-saving changes.

The maximum range is ten calendar years. Unknown fields, missing/invalid filters, equal/reversed instants, and excessive ranges return 422 VALIDATION_ERROR. No pagination, status selector, merchant selector, search query, or as_of input is supported. Merchant context is resolved from the authenticated user: a foreign merchant selector is denied with 403 MERCHANT_SCOPE_MISMATCH. Invalid/missing currency catalogue returns 503 SERVICE_UNAVAILABLE. Both endpoints require the Portal user PAT and enforce active membership, application eligibility and scope checks. Successful responses use Cache-Control: no-store.

Request (no body):

GET /api/v1/portal/collections/metrics?environment=live&currency=PHP&from=2026-09-01T00%3A00%3A00%2B08%3A00&to=2026-10-01T00%3A00%3A00%2B08%3A00&timezone=Asia%2FManila
Authorization: Bearer <user_pat>
Accept: application/json
``` URL-encode `+` as `%2B`.

Charts permit at most 366 daily, 120 monthly, or 10 yearly buckets per chart request, returning `422` with an `interval` field error when exceeded. Limits count all intersected calendar periods, including partial and empty buckets. A ten-year range beginning/ending mid-year can therefore exceed the ten-yearly-bucket limit. Requests are never silently truncated or assigned a different interval. Partial first/last buckets are clipped and marked `partial=true`; empty buckets have zero totals/counts and null success rate.

### 10.2 Metric definitions {#102-metric-definitions}

Metrics count stored collection transactions once across providers, scoped by merchant, environment, currency, optional brand and **creation time** in `[from,to)`. Archived/credited records remain included. Current normalized status determines the result, so historical reports can change as pending transactions resolve. These are creation-cohort metrics, not settlement-date accounting.

| Field | Definition |
| --- | --- |
| `transaction_count` | Number of transactions in the population |
| `successful_count` | Current normalized status `PAID` |
| `pending_count` | Current normalized status `PENDING` |
| `rejected_count` | Current normalized status `REJECTED` |
| `collection_total` | Sum of transaction amounts for `PAID` transactions in the population |
| `success_rate_percent` | `successful_count / transaction_count * 100`, rounded to 2 decimal places; null when count is zero |

Pending and rejected transactions are included in the success-rate denominator. The three status counts must add up to `transaction_count`. This deliberately defines one denominator; do not substitute successful/terminal-only counts without a separately named metric. Return percentage values as decimal strings, for example `"85.00"`.

Collection total is gross successful collection amount before fees; it is not net settlement or refundable balance. Refund/reversal accounting is outside this version. Do not infer success from HTTP status, webhook acknowledgement, non-empty payment messages, or the existing legacy resource's default status alone.

Each summary uses one consistent database read. `as_of` is a UTC observation marker, not a historical filter or reusable snapshot. Separate HTTP reads can observe different states.

Amounts and rates are decimal strings with two fractional digits. Empty populations return `collection_total:"0.00"`, zero counts and `success_rate_percent:null`; nonempty populations without successes return rate `"0.00"`. Paid records with null amounts return `503 SERVICE_UNAVAILABLE` for the entire summary/chart. Pending/rejected null amounts still count.

Chart totals reconcile with their buckets. Overall rates use overall counts, never an average of bucket percentages. Separate summary/chart requests require unchanged data for exact reconciliation; a paid null amount withholds the entire chart with `503`.

**metrics response (synthetic values):**

```json
{
  "success": true,
  "message": "Success",
  "data": {
    "environment": "live",
    "currency": "PHP",
    "timezone": "Asia/Manila",
    "from": "2026-09-01T00:00:00+08:00",
    "to": "2026-10-01T00:00:00+08:00",
    "as_of": "2026-10-01T01:00:00Z",
    "date_basis": "created_at",
    "collection_total": "127500.00",
    "transaction_count": 100,
    "successful_count": 85,
    "pending_count": 10,
    "rejected_count": 5,
    "success_rate_percent": "85.00"
  }
}

10.3 Daily, monthly, and yearly charts

View Example filters Bucket label
Daily interval=day, September 1 through October 1 2026-09-01, 2026-09-02, …
Monthly interval=month, January 1, 2026 through January 1, 2027 2026-01, 2026-02, …
Yearly interval=year, January 1, 2024 through January 1, 2027 2024, 2025, 2026

Dates in this table describe local calendar boundaries; send full timestamps with offsets in the actual query. URL-encode + as %2B. Every chart bucket includes all six metrics, allowing amount, count, and success-rate charts from the same response.

Each bucket represents a local calendar day, month or year, clipped to [from,to). period identifies that calendar period; bucket from/to use the reporting timezone's actual offsets and include six fractional-second digits. An exclusive to exactly at a boundary does not create an extra empty period. Day boundaries use local midnight, month boundaries use the first day, and year boundaries use January 1. Day length may be 23 or 25 hours during daylight-saving changes. Changing timezone may change labels/bucket counts while retaining the same overall population.

Request (no body):

GET /api/v1/portal/collections/charts?environment=test&currency=PHP&interval=day&from=2026-09-01T00%3A00%3A00%2B08%3A00&to=2026-09-02T00%3A00%3A00%2B08%3A00&timezone=Asia%2FManila
Authorization: Bearer <user_pat>
Accept: application/json

one-day chart example (synthetic values):

{
  "success": true,
  "message": "Success",
  "data": {
    "environment": "test",
    "currency": "PHP",
    "timezone": "Asia/Manila",
    "interval": "day",
    "date_basis": "created_at",
    "from": "2026-09-01T00:00:00+08:00",
    "to": "2026-09-02T00:00:00+08:00",
    "as_of": "2026-10-01T01:00:00Z",
    "totals": {
      "collection_total": "1500.00",
      "transaction_count": 3,
      "successful_count": 1,
      "pending_count": 1,
      "rejected_count": 1,
      "success_rate_percent": "33.33"
    },
    "buckets": [
      {
        "period": "2026-09-01",
        "from": "2026-09-01T00:00:00.000000+08:00",
        "to": "2026-09-02T00:00:00.000000+08:00",
        "partial": false,
        "collection_total": "1500.00",
        "transaction_count": 3,
        "successful_count": 1,
        "pending_count": 1,
        "rejected_count": 1,
        "success_rate_percent": "33.33"
      }
    ]
  }
}

Return null success rates for empty buckets; the frontend can show “No transactions.” Zero percent means transactions exist and none are successful.

10.4 Request validation reference

These tables reflect the current implementation’s request rules. See input conventions for required/optional/null behavior and error formats.

Validation: GET /api/v1/portal/collections/metrics

Field Location Requirement Validation rules
environment Query Required String; exactly test or live, case-sensitive.
currency Query Required String; exactly three uppercase ASCII letters and present in DLT’s currency catalogue. Unsupported code returns 422; unavailable catalogue returns 503.
from Query Required String in YYYY-MM-DDTHH:mm:ss[.ffffff]Z or YYYY-MM-DDTHH:mm:ss[.ffffff]±HH:MM format; real calendar date/time, nonzero year, seconds required, optional 1–6 fractional digits, offset up to ±14:00. Date-only, offsetless and impossible instants are invalid.
to Query Required String in YYYY-MM-DDTHH:mm:ss[.ffffff]Z or YYYY-MM-DDTHH:mm:ss[.ffffff]±HH:MM format; real calendar date/time, nonzero year, seconds required, optional 1–6 fractional digits, offset up to ±14:00. Date-only, offsetless and impossible instants are invalid. Strictly later than from; interval must not exceed ten calendar years measured from from in the reporting timezone.
timezone Query Optional; default Asia/Manila Nonempty string; recognized PHP timezone identifier (Laravel timezone validation), such as Asia/Manila; not an arbitrary UTC-offset string.
payment_brand Query Optional; required if supplied Nonempty string, maximum 100 characters; exact stored-brand match.

Send no request body. Use query parameters; null/empty optional values are invalid. Unlisted payload/query fields are rejected with 422 VALIDATION_ERROR. A conflicting merchant selector may instead be rejected earlier with 403 MERCHANT_SCOPE_MISMATCH. interval, pagination, status and merchant-reference filters are not accepted.

Validation: GET /api/v1/portal/collections/charts

Field Location Requirement Validation rules
environment Query Required String; exactly test or live, case-sensitive.
currency Query Required String; exactly three uppercase ASCII letters and present in DLT’s currency catalogue. Unsupported code returns 422; unavailable catalogue returns 503.
from Query Required String in YYYY-MM-DDTHH:mm:ss[.ffffff]Z or YYYY-MM-DDTHH:mm:ss[.ffffff]±HH:MM format; real calendar date/time, nonzero year, seconds required, optional 1–6 fractional digits, offset up to ±14:00. Date-only, offsetless and impossible instants are invalid.
to Query Required String in YYYY-MM-DDTHH:mm:ss[.ffffff]Z or YYYY-MM-DDTHH:mm:ss[.ffffff]±HH:MM format; real calendar date/time, nonzero year, seconds required, optional 1–6 fractional digits, offset up to ±14:00. Date-only, offsetless and impossible instants are invalid. Strictly later than from; interval must not exceed ten calendar years measured from from in the reporting timezone.
timezone Query Optional; default Asia/Manila Nonempty string; recognized PHP timezone identifier (Laravel timezone validation), such as Asia/Manila; not an arbitrary UTC-offset string.
payment_brand Query Optional; required if supplied Nonempty string, maximum 100 characters; exact stored-brand match.
interval Query Required String; exactly day, month or year. Maximum generated calendar buckets: 366 daily, 120 monthly, 10 yearly, including partial periods.

Send no request body. Same ten-calendar-year bound as metrics, plus bucket count in the reporting timezone. Excess buckets return a 422 interval field error. Unlisted payload/query fields are rejected with 422 VALIDATION_ERROR. A conflicting merchant selector may instead be rejected earlier with 403 MERCHANT_SCOPE_MISMATCH.

DLT Developer Documentation · Updated October 2, 2026