Payment API v0 (0.5.13)

Download OpenAPI specification:Download

The Payment API gives you the ability to take payments.

Payment lifecycle

A payment moves through exactly three data.status values:

StatusMeaning
processing The payment is not finished. Either more information is needed from the payer (see data.fields), or a follow-up PATCH is required to execute it, or it is settling.
complete The payment succeeded.
declined The processor declined the payment. data.reason describes why. A declined payment can be re-attempted with PATCH /{id} — you do not need to create a new payment.

The intended flow is create once, then update the same payment:

  1. POST / creates the payment. The response is always HTTP 201, even when the payment is declined — inspect data.status, not the HTTP code.
  2. If the response contains data.fields, the card requires additional prompts (e.g. hubReading, tripNumber). Collect the required: true fields from the driver and send them with PATCH /{id}. Do not create a new payment.
  3. For efscheck and tchek payments, POST only creates the check (status: processing); the charge is executed by a follow-up PATCH /{id} carrying the expressCode.
  4. If data.status is declined, you may correct the inputs and re-attempt the same payment with PATCH /{id}.
  5. GET /{id} returns the live status at any time.
        POST /  (create payment — also creates the RoadSync invoice)
          │
          ▼  HTTP 201 — always; read data.status
    ┌─────────────┬──────────────────────┬─────────────┐
    ▼             ▼                      ▼             ▼
 complete    processing            processing       declined
   DONE     + data.fields         (efscheck/tchek)  + reason
                 │                      │             │
                 ▼                      ▼             ▼
          collect required        collect the      fix inputs
          prompts from driver     expressCode          │
                 │                      │             │
                 └──────────┬───────────┴─────────────┘
                            ▼
                    PATCH /{id}  (same payment — never POST again)
                            │
                            ▼  HTTP 200 → complete | declined (+reason)
                            │            HTTP 400 → still-missing prompts
                            └─── repeat PATCH with corrections as needed

Creating vs. updating — important

Every POST / creates a new RoadSync invoice on the merchant's account (for all payment types except cardonfile, which pays an existing invoice you reference by invoiceId). Requests are not idempotent: retrying a POST — including after a decline or a fields response — creates a duplicate invoice on the merchant's account and can result in a double charge. Always retry and complete payments via PATCH /{id}; only use a new POST for a genuinely new payment.

A payment cannot be updated after it is complete (PATCH returns 400 with code invoice_status_invalid). cardonfile payments cannot be updated.

Errors and decline reasons

Successful calls return { "data": { ... }, "errors": [ ... ] }; failed calls return { "errors": [{ "code", "title" }] }. Validation error codes follow the convention {field}_required and {field}_invalid (e.g. unit_number_required, hub_reading_required, location_id_invalid).

data.reason is the decline reason from the payment processor, passed through from the RoadSync platform (e.g. funds, unabletoprocess, amountexceedsmaximum, WexInvalidUnitNumber, ComdataTractorHubRequired). Treat it as a free-form string: for some processor declines it is expanded into a sentence that includes the processor's own message (Error: <reason>, Message from Comdata: <message>). A reason may also accompany status: processing when a prompt was rejected and the payment is awaiting a corrected PATCH.

What to show the payer

  • Show errors[].title. It is written for a person to read and is safe to display as-is.
  • Branch on errors[].code, not on title. Codes are stable; title wording may change between releases.
  • Do not display data.reason. It is a diagnostic passthrough of raw processor strings, and some values describe card restrictions that should not be revealed at the point of sale. Log it and quote it in support requests.
  • When a payment is declined and no errors[] are returned, show your own generic message rather than the reason.

You do not need to enumerate every error code. Handle the three outcomes below and your integration stays correct as processors add new reasons:

ResponseWhat it meansWhat to do
data.fields present More input is needed Collect the required: true prompts, then PATCH /{id}
errors[] present The request was rejected Show the titles, correct the input, retry
status: declined The processor said no Show a generic decline, log reason, offer another payment method

Treat anything you don't recognise as a generic decline.

Payment types at a glance

Payment type Creates invoice Returns fields Executes on POST PATCH supported
creditcardYesNoYesYes
comdatafuelcardYesYes Only when no fields are outstanding Yes — re-validates prompts, then executes
wexfuelcardYesNoYesYes
comdataexpresscodeYesNoYesYes
efscheck / tchekYesNo NoPATCH with expressCode executes it Yes (required)
cardonfile No — pays existing invoiceId NoYesNo
Copyright © RoadSync 2023. All right reserved.