Documentation
¶
Overview ¶
Package checkslip is the official Go client for the Checkslip API (https://checkslip.com/docs).
Checkslip looks up a Thai bank transfer from its slip. Send the QR text on the slip, or a picture of the slip, and Checkslip asks the sending bank for the transfer it holds and returns that data. Whether to accept the payment is your decision: compare the amount, receiver and time with your order.
Getting started ¶
client := checkslip.NewClient(os.Getenv("CHECKSLIP_API_KEY"))
res, err := client.Slips.Lookup(ctx, &checkslip.LookupParams{QR: qrText})
if err != nil {
return err
}
switch res.Outcome {
case checkslip.OutcomeFound:
fmt.Println(res.Slip.Amount, res.Slip.TransferredAt)
case checkslip.OutcomePending:
// Read it later with client.Slips.Get(ctx, res.RequestID).
}
Errors ¶
Every response with status 400 or above is returned as an *Error that holds the problem details the API sent (RFC 9457): StatusCode, Code, Title, Detail and RequestID. Use errors.As, AsError, HasCode or helpers such as IsRateLimited and IsNoCredit to tell failures apart.
Retries ¶
The client sends a failed request again, up to WithMaxRetries times (2 by default), with exponential backoff and jitter:
- 429 Too Many Requests is always retried, after the Retry-After the API asked for.
- 500, 502, 503 and 504 and network failures are retried only when the request is safe to repeat: GET, PUT and DELETE, or any request that carries an Idempotency-Key.
- 409 IDEMPOTENCY_IN_FLIGHT is retried with the same key.
A Retry-After longer than 60 seconds, or longer than the time left on the context, is not waited out; the *Error is returned with RetryAfter set.
Idempotency ¶
Slips.Lookup and every call the API requires a key for send an Idempotency-Key. The client generates a random one unless you pass WithIdempotencyKey. A request sent again with the same key returns the first result, and a lookup is never charged twice.
Pagination ¶
List methods return one Page. ListAll methods return an Iter that follows next_cursor across pages; on Go 1.23 or later, Iter.All works with range.
Webhooks ¶
Messages follow Standard Webhooks v1. Check each one with WebhookVerifier.VerifyRequest before you act on it, and deduplicate on its webhook-id because a message can arrive more than once.
Index ¶
- Constants
- Variables
- func HasCode(err error, code ErrorCode) bool
- func IsForbidden(err error) bool
- func IsNoCredit(err error) bool
- func IsNotFound(err error) bool
- func IsRateLimited(err error) bool
- func IsServerError(err error) bool
- func IsUnauthenticated(err error) bool
- func IsValidation(err error) bool
- func NewIdempotencyKey() string
- func VerifyWebhook(secret string, header http.Header, body []byte) error
- type APIRequestRecord
- type ActorType
- type AttemptOutcome
- type AttemptTrigger
- type AuditActor
- type AuditCategory
- type AuditEventRecord
- type AuditEventsService
- func (s *AuditEventsService) Get(ctx context.Context, eventID string, opts ...RequestOption) (*AuditEventRecord, error)
- func (s *AuditEventsService) List(ctx context.Context, params *ListAuditEventsParams, opts ...RequestOption) (*Page[AuditEventRecord], error)
- func (s *AuditEventsService) ListAll(ctx context.Context, params *ListAuditEventsParams, opts ...RequestOption) *Iter[AuditEventRecord]
- type AuditExportsService
- func (s *AuditExportsService) Create(ctx context.Context, params *CreateEvidenceExportParams, opts ...RequestOption) (*EvidenceExport, error)
- func (s *AuditExportsService) Download(ctx context.Context, exportID string, opts ...RequestOption) (*EvidenceFile, error)
- func (s *AuditExportsService) Get(ctx context.Context, exportID string, opts ...RequestOption) (*EvidenceExport, error)
- func (s *AuditExportsService) List(ctx context.Context, params *ListOptions, opts ...RequestOption) (*Page[EvidenceExport], error)
- func (s *AuditExportsService) ListAll(ctx context.Context, params *ListOptions, opts ...RequestOption) *Iter[EvidenceExport]
- type AuditOutcome
- type AuditRequestsService
- func (s *AuditRequestsService) Get(ctx context.Context, requestID string, opts ...RequestOption) (*APIRequestRecord, error)
- func (s *AuditRequestsService) List(ctx context.Context, params *ListRequestsParams, opts ...RequestOption) (*Page[APIRequestRecord], error)
- func (s *AuditRequestsService) ListAll(ctx context.Context, params *ListRequestsParams, opts ...RequestOption) *Iter[APIRequestRecord]
- type AuditService
- type AuditTarget
- type Balance
- type Bank
- type BanksService
- type BillingBalanceService
- type BillingInfo
- type BillingInfoService
- type BillingLedgerService
- type BillingOrdersService
- func (s *BillingOrdersService) Get(ctx context.Context, orderID string, opts ...RequestOption) (*Order, error)
- func (s *BillingOrdersService) List(ctx context.Context, params *OrdersParams, opts ...RequestOption) (*Page[Order], error)
- func (s *BillingOrdersService) ListAll(ctx context.Context, params *OrdersParams, opts ...RequestOption) *Iter[Order]
- func (s *BillingOrdersService) Sync(ctx context.Context, orderID string, opts ...RequestOption) (*Order, error)
- type BillingPackagesService
- type BillingReceiptsService
- func (s *BillingReceiptsService) Get(ctx context.Context, receiptID string, opts ...RequestOption) (*Receipt, error)
- func (s *BillingReceiptsService) List(ctx context.Context, params *ListOptions, opts ...RequestOption) (*Page[Receipt], error)
- func (s *BillingReceiptsService) ListAll(ctx context.Context, params *ListOptions, opts ...RequestOption) *Iter[Receipt]
- type BillingService
- type BuyerType
- type Channel
- type ChargedFilter
- type Client
- type CountBucket
- type CountSeries
- type CreateEndpointParams
- type CreateEvidenceExportParams
- type CreditEntry
- type CreditReason
- type Date
- type Delivery
- type DeliveryAttempt
- type DeliveryDetail
- type DeliveryStatus
- type DisabledReason
- type DuplicateOf
- type Endpoint
- type EndpointWithSecret
- type Error
- type ErrorCode
- type EventType
- type EventTypeInfo
- type EvidenceExport
- type EvidenceFile
- type ExportColumn
- type ExportCreator
- type ExportManifest
- type ExportMode
- type ExportPart
- type ExportState
- type FieldError
- type Fingerprint
- type FreeReason
- type Iter
- type LedgerParams
- type ListAuditEventsParams
- type ListDeliveriesParams
- type ListOptions
- type ListRequestsParams
- type ListSlipsParams
- type LookupParams
- type Money
- type Option
- type Order
- type OrderStatus
- type OrdersParams
- type Outcome
- type Package
- type PackageList
- type Page
- type Party
- type PaymentMethod
- type Receipt
- type ReceiptLine
- type ReceiptSeller
- type ReceiptVAT
- type Receiver
- type ReceiverKind
- type RecoverParams
- type RecoverResult
- type RequestOption
- type SecretRotation
- type Slip
- type SlipCheck
- type SlipCheckRecord
- type SlipCheckedData
- type SlipCounts
- type SlipCountsParams
- type SlipExport
- type SlipExportParams
- type SlipExportsService
- func (s *SlipExportsService) Create(ctx context.Context, params *SlipExportParams, opts ...RequestOption) (*SlipExport, error)
- func (s *SlipExportsService) DownloadPart(ctx context.Context, part ExportPart, opts ...RequestOption) (io.ReadCloser, error)
- func (s *SlipExportsService) Get(ctx context.Context, exportID string, opts ...RequestOption) (*SlipExport, error)
- type SlipsService
- func (s *SlipsService) Counts(ctx context.Context, params *SlipCountsParams, opts ...RequestOption) (*SlipCounts, error)
- func (s *SlipsService) Get(ctx context.Context, requestID string, opts ...RequestOption) (*SlipCheckRecord, error)
- func (s *SlipsService) List(ctx context.Context, params *ListSlipsParams, opts ...RequestOption) (*Page[SlipCheckRecord], error)
- func (s *SlipsService) ListAll(ctx context.Context, params *ListSlipsParams, opts ...RequestOption) *Iter[SlipCheckRecord]
- func (s *SlipsService) Lookup(ctx context.Context, params *LookupParams, opts ...RequestOption) (*SlipCheck, error)
- type Stream
- type SupportedBank
- type ThresholdBasis
- type UpdateEndpointParams
- type Usage
- type UsageBucket
- type UsageCounts
- type UsageInterval
- type UsageParams
- type UsageService
- type WebhookDeliveriesService
- func (s *WebhookDeliveriesService) Get(ctx context.Context, deliveryID string, opts ...RequestOption) (*DeliveryDetail, error)
- func (s *WebhookDeliveriesService) List(ctx context.Context, params *ListDeliveriesParams, opts ...RequestOption) (*Page[Delivery], error)
- func (s *WebhookDeliveriesService) ListAll(ctx context.Context, params *ListDeliveriesParams, opts ...RequestOption) *Iter[Delivery]
- func (s *WebhookDeliveriesService) Redeliver(ctx context.Context, deliveryID string, opts ...RequestOption) (*Delivery, error)
- type WebhookEndpointsService
- func (s *WebhookEndpointsService) Create(ctx context.Context, params *CreateEndpointParams, opts ...RequestOption) (*EndpointWithSecret, error)
- func (s *WebhookEndpointsService) Delete(ctx context.Context, endpointID string, opts ...RequestOption) error
- func (s *WebhookEndpointsService) Get(ctx context.Context, endpointID string, opts ...RequestOption) (*Endpoint, error)
- func (s *WebhookEndpointsService) List(ctx context.Context, params *ListOptions, opts ...RequestOption) (*Page[Endpoint], error)
- func (s *WebhookEndpointsService) ListAll(ctx context.Context, params *ListOptions, opts ...RequestOption) *Iter[Endpoint]
- func (s *WebhookEndpointsService) Recover(ctx context.Context, endpointID string, params *RecoverParams, ...) (*RecoverResult, error)
- func (s *WebhookEndpointsService) RotateSecret(ctx context.Context, endpointID string, opts ...RequestOption) (*SecretRotation, error)
- func (s *WebhookEndpointsService) SendTest(ctx context.Context, endpointID string, opts ...RequestOption) (*Delivery, error)
- func (s *WebhookEndpointsService) Update(ctx context.Context, endpointID string, params *UpdateEndpointParams, ...) (*Endpoint, error)
- type WebhookEvent
- type WebhookEventTypesService
- type WebhookTestData
- type WebhookVerifier
- type WebhooksService
Examples ¶
Constants ¶
const DefaultBaseURL = "https://api.checkslip.com"
DefaultBaseURL is the Checkslip API address used when WithBaseURL is not given.
const DefaultWebhookTolerance = 5 * time.Minute
DefaultWebhookTolerance is how far webhook-timestamp may be from the receiver's clock before a message is refused, to stop replays.
const MaxImageSize = 10 << 20
MaxImageSize is the largest slip image the API accepts, 10 MB.
const MaxWebhookBody = 1 << 20
MaxWebhookBody is the largest body VerifyRequest reads.
const Version = "0.3.0" // x-release-please-version
Version is the version of this SDK. It is sent in the User-Agent header.
Variables ¶
var ( // ErrWebhookMissingHeaders means a Standard Webhooks header is absent. ErrWebhookMissingHeaders = errors.New("checkslip: webhook headers missing") // ErrWebhookTimestamp means webhook-timestamp is unreadable or outside // the tolerance. ErrWebhookTimestamp = errors.New("checkslip: webhook timestamp outside tolerance") // ErrWebhookSignature means no signature matched any secret. ErrWebhookSignature = errors.New("checkslip: webhook signature does not match") )
Webhook verification errors. Compare with errors.Is. Answer 400 or 401 to a message that fails, and do not process it.
Functions ¶
func IsForbidden ¶
IsForbidden reports whether the API key lacks the permission for the call (403).
func IsNoCredit ¶
IsNoCredit reports whether a lookup was refused because the account has no credit left or its credit expired (402).
func IsNotFound ¶
IsNotFound reports whether the requested record does not exist for this account (404).
func IsRateLimited ¶
IsRateLimited reports whether the API answered 429 Too Many Requests. The error's RetryAfter says how long to wait.
func IsServerError ¶
IsServerError reports whether the API failed on its side (status 500 or above). The client already retried it when that was safe.
func IsUnauthenticated ¶
IsUnauthenticated reports whether the API key was missing, wrong or revoked (401).
func IsValidation ¶
IsValidation reports whether the request was rejected as malformed (400 or 422). Check Error.Code and Error.Fields for the reason.
func NewIdempotencyKey ¶
func NewIdempotencyKey() string
NewIdempotencyKey returns a random UUID (version 4) to use as an Idempotency-Key. The client calls it for you when a method needs a key and you did not pass one with WithIdempotencyKey.
Types ¶
type APIRequestRecord ¶
type APIRequestRecord struct {
RequestID string `json:"request_id"`
ReceivedAt time.Time `json:"received_at"`
// LatencyMs is how long this request took, from receiving it to
// answering it.
LatencyMs int `json:"latency_ms"`
// Channel may be nil.
Channel *Channel `json:"channel"`
Method string `json:"method"`
Route string `json:"route"`
HTTPStatus int `json:"http_status"`
APIKeyID *string `json:"api_key_id,omitempty"`
UserID *string `json:"user_id,omitempty"`
// ClientIPPrefix is the caller's network (/24 or /48).
ClientIPPrefix string `json:"client_ip_prefix,omitempty"`
// ClientIPHMAC is a keyed hash of the full address, to match requests
// from the same machine.
ClientIPHMAC string `json:"client_ip_hmac,omitempty"`
UserAgent string `json:"user_agent,omitempty"`
OutcomeCode string `json:"outcome_code,omitempty"`
Fingerprint *Fingerprint `json:"fingerprint,omitempty"`
}
APIRequestRecord is the record of one request this account sent. Records hold no names, phone numbers, emails or full account numbers.
type AttemptOutcome ¶
type AttemptOutcome string
AttemptOutcome is the result of one HTTP attempt of a webhook delivery.
const ( AttemptSuccess AttemptOutcome = "SUCCESS" AttemptHTTPError AttemptOutcome = "HTTP_ERROR" AttemptTimeout AttemptOutcome = "TIMEOUT" AttemptConnectError AttemptOutcome = "CONNECT_ERROR" AttemptTLSError AttemptOutcome = "TLS_ERROR" AttemptRedirectNotFollowed AttemptOutcome = "REDIRECT_NOT_FOLLOWED" AttemptDestinationBlocked AttemptOutcome = "DESTINATION_BLOCKED" )
Attempt outcomes.
type AttemptTrigger ¶
type AttemptTrigger string
AttemptTrigger says what started a webhook attempt.
const ( TriggerSchedule AttemptTrigger = "SCHEDULE" TriggerRedeliver AttemptTrigger = "REDELIVER" TriggerRecover AttemptTrigger = "RECOVER" )
Attempt triggers.
type AuditActor ¶
type AuditActor struct {
Type ActorType `json:"type"`
ID *string `json:"id,omitempty"`
OnBehalfOfUser *string `json:"on_behalf_of_user_id,omitempty"`
}
AuditActor is who performed an audited action.
type AuditCategory ¶
type AuditCategory string
AuditCategory groups audit events.
const ( AuditMoney AuditCategory = "MONEY" AuditAccess AuditCategory = "ACCESS" AuditDataChange AuditCategory = "DATA_CHANGE" AuditSensitiveRead AuditCategory = "SENSITIVE_READ" AuditSecurity AuditCategory = "SECURITY" )
Audit categories.
type AuditEventRecord ¶
type AuditEventRecord struct {
EventID string `json:"event_id"`
Category AuditCategory `json:"category"`
Outcome AuditOutcome `json:"outcome"`
OccurredAt time.Time `json:"occurred_at"`
// RequestID is the API request that caused this event, if any.
RequestID *string `json:"request_id,omitempty"`
Actor AuditActor `json:"actor"`
Channel Channel `json:"channel"`
Action string `json:"action"`
Target AuditTarget `json:"target"`
// Before and After are the values before and after the change, as raw
// JSON objects.
Before map[string]any `json:"before,omitempty"`
After map[string]any `json:"after,omitempty"`
Reason *string `json:"reason,omitempty"`
UndoOfEventID *string `json:"undo_of_event_id,omitempty"`
}
AuditEventRecord is one action on the account, such as an API key created or a member removed.
type AuditEventsService ¶
type AuditEventsService struct {
// contains filtered or unexported fields
}
AuditEventsService reads account activity.
func (*AuditEventsService) Get ¶
func (s *AuditEventsService) Get(ctx context.Context, eventID string, opts ...RequestOption) (*AuditEventRecord, error)
Get returns one activity record.
func (*AuditEventsService) List ¶
func (s *AuditEventsService) List(ctx context.Context, params *ListAuditEventsParams, opts ...RequestOption) (*Page[AuditEventRecord], error)
List returns one page of account activity, newest first.
func (*AuditEventsService) ListAll ¶
func (s *AuditEventsService) ListAll(ctx context.Context, params *ListAuditEventsParams, opts ...RequestOption) *Iter[AuditEventRecord]
ListAll walks every activity record that matches params.
type AuditExportsService ¶
type AuditExportsService struct {
// contains filtered or unexported fields
}
AuditExportsService builds and downloads evidence files.
func (*AuditExportsService) Create ¶
func (s *AuditExportsService) Create(ctx context.Context, params *CreateEvidenceExportParams, opts ...RequestOption) (*EvidenceExport, error)
Create starts building an evidence file. It returns at once with State QUEUED; poll GetExport until it is READY or FAILED. The call always carries an Idempotency-Key, so a retry returns the same file.
func (*AuditExportsService) Download ¶
func (s *AuditExportsService) Download(ctx context.Context, exportID string, opts ...RequestOption) (*EvidenceFile, error)
Download downloads a READY evidence file. Before it is READY the API answers 409; after ExpiresAt it answers 410. Every download is written to the account activity.
func (*AuditExportsService) Get ¶
func (s *AuditExportsService) Get(ctx context.Context, exportID string, opts ...RequestOption) (*EvidenceExport, error)
Get returns the state of one evidence file.
func (*AuditExportsService) List ¶
func (s *AuditExportsService) List(ctx context.Context, params *ListOptions, opts ...RequestOption) (*Page[EvidenceExport], error)
List returns one page of evidence files, newest first.
func (*AuditExportsService) ListAll ¶
func (s *AuditExportsService) ListAll(ctx context.Context, params *ListOptions, opts ...RequestOption) *Iter[EvidenceExport]
ListAll walks every evidence file.
type AuditOutcome ¶
type AuditOutcome string
AuditOutcome is how an audited action ended.
const ( AuditSuccess AuditOutcome = "SUCCESS" AuditFailure AuditOutcome = "FAILURE" AuditDenied AuditOutcome = "DENIED" )
Audit outcomes.
type AuditRequestsService ¶
type AuditRequestsService struct {
// contains filtered or unexported fields
}
AuditRequestsService reads the account's request log.
func (*AuditRequestsService) Get ¶
func (s *AuditRequestsService) Get(ctx context.Context, requestID string, opts ...RequestOption) (*APIRequestRecord, error)
Get returns one request record.
func (*AuditRequestsService) List ¶
func (s *AuditRequestsService) List(ctx context.Context, params *ListRequestsParams, opts ...RequestOption) (*Page[APIRequestRecord], error)
List returns one page of the requests this account sent, newest first.
func (*AuditRequestsService) ListAll ¶
func (s *AuditRequestsService) ListAll(ctx context.Context, params *ListRequestsParams, opts ...RequestOption) *Iter[APIRequestRecord]
ListAll walks every request record that matches params.
type AuditService ¶
type AuditService struct {
// Events is account activity, such as API keys created or members removed.
Events *AuditEventsService
// Requests is the log of requests this account sent.
Requests *AuditRequestsService
// Exports builds evidence files for an auditor, a bank or a court.
Exports *AuditExportsService
// contains filtered or unexported fields
}
AuditService reads the account's request records and activity, and builds evidence files of them for an auditor, a bank or a court. It needs an API key with audit:read; evidence files also need audit:export.
type AuditTarget ¶
AuditTarget is what an audited action was done to.
type Balance ¶
type Balance struct {
AccountID string `json:"account_id"`
// Credits is the number of lookups left. One FOUND lookup uses one.
Credits int64 `json:"credits"`
// Low is true when Credits has fallen to Threshold or below. Show it as
// it is rather than computing your own threshold.
Low bool `json:"low"`
Threshold int64 `json:"threshold"`
Basis ThresholdBasis `json:"threshold_basis"`
// ExpiresAt is when the remaining credits expire, or nil.
ExpiresAt *time.Time `json:"expires_at"`
UpdatedAt time.Time `json:"updated_at"`
LastEntryID *string `json:"last_entry_id"`
}
Balance is the account's credit balance.
type Bank ¶
type Bank struct {
// Code is Checkslip's short code, for example "KBANK".
Code string `json:"code"`
// BOTCode is the three-digit Bank of Thailand code, for example "004".
BOTCode string `json:"bot_code"`
// NameTH is the bank's Thai name, when known.
NameTH *string `json:"name_th"`
// NameEN is the bank's English name, when known.
NameEN *string `json:"name_en"`
}
Bank identifies a Thai bank.
type BanksService ¶
type BanksService struct {
// contains filtered or unexported fields
}
BanksService lists the banks whose transfers can be looked up.
func (*BanksService) List ¶
func (s *BanksService) List(ctx context.Context, opts ...RequestOption) ([]SupportedBank, error)
List returns the supported banks. It needs no API key.
type BillingBalanceService ¶
type BillingBalanceService struct {
// contains filtered or unexported fields
}
BillingBalanceService reads the credit balance.
func (*BillingBalanceService) Get ¶
func (s *BillingBalanceService) Get(ctx context.Context, opts ...RequestOption) (*Balance, error)
Get returns the remaining credits, their expiry and whether they are running low. It needs an API key with credit:read or billing:read.
type BillingInfo ¶
type BillingInfo struct {
BuyerType BuyerType `json:"buyer_type,omitempty"`
Name string `json:"name,omitempty"`
TaxID string `json:"tax_id,omitempty"`
Branch string `json:"branch,omitempty"`
Address string `json:"address,omitempty"`
Email string `json:"email,omitempty"`
}
BillingInfo is the buyer shown on receipts.
type BillingInfoService ¶
type BillingInfoService struct {
// contains filtered or unexported fields
}
BillingInfoService reads and replaces the billing details on receipts.
func (*BillingInfoService) Get ¶
func (s *BillingInfoService) Get(ctx context.Context, opts ...RequestOption) (*BillingInfo, error)
Get returns the billing details used on receipts.
func (*BillingInfoService) Update ¶
func (s *BillingInfoService) Update(ctx context.Context, info *BillingInfo, opts ...RequestOption) (*BillingInfo, error)
Update saves the buyer details printed on your next receipts. Receipts already issued keep theirs. The tax ID must be 13 digits. It needs an API key with billing:write.
type BillingLedgerService ¶
type BillingLedgerService struct {
// contains filtered or unexported fields
}
BillingLedgerService reads the credit ledger.
func (*BillingLedgerService) List ¶
func (s *BillingLedgerService) List(ctx context.Context, params *LedgerParams, opts ...RequestOption) (*Page[CreditEntry], error)
List returns one page of credit balance changes, newest first: purchases, lookups that used a credit, lookups that did not (with the reason in FreeReason) and credits that expired. One call covers at most 31 days.
func (*BillingLedgerService) ListAll ¶
func (s *BillingLedgerService) ListAll(ctx context.Context, params *LedgerParams, opts ...RequestOption) *Iter[CreditEntry]
ListAll walks every ledger entry that matches params.
type BillingOrdersService ¶
type BillingOrdersService struct {
// contains filtered or unexported fields
}
BillingOrdersService reads orders and refreshes their payment result.
func (*BillingOrdersService) Get ¶
func (s *BillingOrdersService) Get(ctx context.Context, orderID string, opts ...RequestOption) (*Order, error)
Get returns one credit order.
func (*BillingOrdersService) List ¶
func (s *BillingOrdersService) List(ctx context.Context, params *OrdersParams, opts ...RequestOption) (*Page[Order], error)
List returns one page of credit orders, newest first.
func (*BillingOrdersService) ListAll ¶
func (s *BillingOrdersService) ListAll(ctx context.Context, params *OrdersParams, opts ...RequestOption) *Iter[Order]
ListAll walks every order that matches params.
func (*BillingOrdersService) Sync ¶
func (s *BillingOrdersService) Sync(ctx context.Context, orderID string, opts ...RequestOption) (*Order, error)
Sync asks the payment provider for the payment result of an order now instead of waiting for its notice, and returns the order. If it is paid, the credits arrive then. If the payment page was closed but the money has not arrived, the order shows Confirming. Asking again within a few seconds returns the same result.
type BillingPackagesService ¶
type BillingPackagesService struct {
// contains filtered or unexported fields
}
BillingPackagesService lists the credit packages on sale.
func (*BillingPackagesService) List ¶
func (s *BillingPackagesService) List(ctx context.Context, opts ...RequestOption) (*PackageList, error)
List returns every credit package with its price, credits and validity. One credit covers one transfer lookup.
type BillingReceiptsService ¶
type BillingReceiptsService struct {
// contains filtered or unexported fields
}
BillingReceiptsService reads receipts.
func (*BillingReceiptsService) Get ¶
func (s *BillingReceiptsService) Get(ctx context.Context, receiptID string, opts ...RequestOption) (*Receipt, error)
Get returns one receipt.
func (*BillingReceiptsService) List ¶
func (s *BillingReceiptsService) List(ctx context.Context, params *ListOptions, opts ...RequestOption) (*Page[Receipt], error)
List returns one page of receipts, newest first.
func (*BillingReceiptsService) ListAll ¶
func (s *BillingReceiptsService) ListAll(ctx context.Context, params *ListOptions, opts ...RequestOption) *Iter[Receipt]
ListAll walks every receipt.
type BillingService ¶
type BillingService struct {
// Balance is the credit left on the account.
Balance *BillingBalanceService
// Info is the billing details printed on receipts.
Info *BillingInfoService
// List are the credit packages on sale.
Packages *BillingPackagesService
// List is every top-up, charge and expiry.
Ledger *BillingLedgerService
// List are orders for credit packages.
Orders *BillingOrdersService
// List are receipts for paid orders.
Receipts *BillingReceiptsService
// contains filtered or unexported fields
}
BillingService reads credits, orders, receipts and the billing details printed on receipts.
type BuyerType ¶
type BuyerType string
BuyerType says whether the buyer on a receipt is a person or a company.
type ChargedFilter ¶
type ChargedFilter string
ChargedFilter selects lookups by whether they used a credit.
const ( ChargedYes ChargedFilter = "true" ChargedNo ChargedFilter = "false" ChargedUnsettled ChargedFilter = "unsettled" )
Charged filters for ListSlipsParams.Charged.
type Client ¶
type Client struct {
// Slips looks up slips and reads the lookup history.
Slips *SlipsService
// Billing reads credits, orders, receipts and billing details.
Billing *BillingService
// Usage reads lookup counts per day or month.
Usage *UsageService
// Banks lists the banks whose transfers can be looked up.
Banks *BanksService
// Webhooks manages webhook endpoints and their deliveries.
Webhooks *WebhooksService
// Audit reads request records and activity and builds evidence files.
Audit *AuditService
// contains filtered or unexported fields
}
Client talks to the Checkslip API. Create one with NewClient and share it: a Client is safe for concurrent use by multiple goroutines.
The API is grouped into services that hang off the Client, for example client.Slips.Lookup or client.Billing.Balance.
func NewClient ¶
NewClient returns a Client that authenticates with apiKey, a key that starts with "csk_". Create keys in the Checkslip dashboard.
NewClient does not contact the API. A bad option, such as an unparsable base URL, is reported by the first request.
Example ¶
package main
import (
"os"
"time"
checkslip "github.com/Checkslip/sdk-go"
)
func main() {
client := checkslip.NewClient(os.Getenv("CHECKSLIP_API_KEY"),
checkslip.WithTimeout(10*time.Second),
checkslip.WithMaxRetries(3),
checkslip.WithUserAgent("my-shop/1.4"),
)
_ = client
}
Output:
type CountBucket ¶
type CountBucket string
CountBucket is the bucket size of SlipsService.Counts.
const ( BucketMinute CountBucket = "MINUTE" BucketFiveMinutes CountBucket = "FIVE_MINUTE" BucketFifteenMinutes CountBucket = "FIFTEEN_MINUTE" BucketHour CountBucket = "HOUR" BucketThreeHours CountBucket = "THREE_HOUR" BucketSixHours CountBucket = "SIX_HOUR" BucketTwelveHours CountBucket = "TWELVE_HOUR" BucketDay CountBucket = "DAY" )
Count buckets.
type CountSeries ¶
CountSeries is the count of one bucket, keyed by outcome.
type CreateEndpointParams ¶
type CreateEndpointParams struct {
// URL must be https without user:pass@, and every address its domain
// resolves to must be public. Required.
URL string `json:"url"`
// Description is a note for people. Optional.
Description string `json:"description,omitempty"`
// EventTypes lists the messages to send. Required.
EventTypes []EventType `json:"event_types"`
}
CreateEndpointParams is the input of WebhookEndpointsService.Create.
type CreateEvidenceExportParams ¶
type CreateEvidenceExportParams struct {
// FromDay and ToDay are UTC days, at most 31 days apart. Only closed
// days can be exported: not today, and yesterday only from 00:10 UTC.
FromDay Date `json:"from_day"`
ToDay Date `json:"to_day"`
// Streams picks request records, activity or both. Empty means both.
Streams []Stream `json:"streams,omitempty"`
}
CreateEvidenceExportParams is the input of AuditService.CreateExport.
type CreditEntry ¶
type CreditEntry struct {
ID string `json:"id"`
// Delta is the change: positive for credits added, negative for used
// or expired.
Delta int64 `json:"delta"`
BalanceAfter int64 `json:"balance_after"`
ExpiresAtAfter *time.Time `json:"expires_at_after,omitempty"`
Reason CreditReason `json:"reason"`
FreeReason FreeReason `json:"free_reason,omitempty"`
// RefType and RefID point at what caused the change, such as an order
// or a lookup.
RefType string `json:"ref_type,omitempty"`
RefID string `json:"ref_id,omitempty"`
CreatedAt time.Time `json:"created_at"`
}
CreditEntry is one change of the credit balance.
type CreditReason ¶
type CreditReason string
CreditReason says why the credit balance changed.
const ( CreditTesterGrant CreditReason = "TESTER_GRANT" CreditPurchase CreditReason = "PURCHASE" CreditSlipCharge CreditReason = "SLIP_CHARGE" CreditSlipFree CreditReason = "SLIP_FREE" CreditExpire CreditReason = "EXPIRE" CreditStaffAdjust CreditReason = "STAFF_ADJUST" CreditAccountDeleted CreditReason = "ACCOUNT_DELETED" CreditDevGrant CreditReason = "DEV_GRANT" )
Credit reasons.
type Date ¶
Date is a calendar day without a time or time zone, sent as "2006-01-02".
func (Date) MarshalText ¶
MarshalText implements encoding.TextMarshaler.
func (*Date) UnmarshalText ¶
UnmarshalText implements encoding.TextUnmarshaler.
type Delivery ¶
type Delivery struct {
ID string `json:"id"`
// MessageID is the webhook-id header of the message. It stays the same
// on every attempt and resend; deduplicate on it.
MessageID string `json:"message_id"`
EndpointID string `json:"endpoint_id"`
EventType EventType `json:"event_type"`
Status DeliveryStatus `json:"status"`
AttemptCount int `json:"attempt_count"`
NextAttemptAt *time.Time `json:"next_attempt_at"`
LastAttemptAt *time.Time `json:"last_attempt_at"`
LastHTTPStatus *int `json:"last_http_status"`
CreatedAt time.Time `json:"created_at"`
}
Delivery is one message sent, or being sent, to one endpoint.
type DeliveryAttempt ¶
type DeliveryAttempt struct {
AttemptNo int `json:"attempt_no"`
Trigger AttemptTrigger `json:"trigger,omitempty"`
AttemptedAt time.Time `json:"attempted_at"`
Outcome AttemptOutcome `json:"outcome"`
HTTPStatus *int `json:"http_status"`
DurationMS int64 `json:"duration_ms"`
// ResponseExcerpt is the start of your endpoint's response body.
ResponseExcerpt *string `json:"response_excerpt"`
}
DeliveryAttempt is one HTTP attempt of a delivery.
type DeliveryDetail ¶
type DeliveryDetail struct {
Delivery
// Payload is the message body, or nil once it has expired. Decode it
// with ParseWebhookEvent.
Payload json.RawMessage `json:"payload"`
PayloadExpiresAt time.Time `json:"payload_expires_at"`
Attempts []DeliveryAttempt `json:"attempts"`
}
DeliveryDetail is a delivery with its payload and every attempt.
type DeliveryStatus ¶
type DeliveryStatus string
DeliveryStatus is the state of one webhook delivery.
const ( DeliveryPending DeliveryStatus = "PENDING" DeliveryRetrying DeliveryStatus = "RETRYING" DeliverySucceeded DeliveryStatus = "SUCCEEDED" // DeliveryFailed means 8 attempts over about 27.5 hours got no 2xx, or // your endpoint answered 410 Gone. DeliveryFailed DeliveryStatus = "FAILED" DeliveryCancelled DeliveryStatus = "CANCELLED" )
Delivery statuses.
type DisabledReason ¶
type DisabledReason string
DisabledReason says why a webhook endpoint stopped receiving messages.
const ( // DisabledManual means someone set enabled to false. DisabledManual DisabledReason = "MANUAL" // DisabledGone means the endpoint answered 410 Gone. DisabledGone DisabledReason = "GONE" // DisabledFailing means every delivery failed for 5 days in a row. DisabledFailing DisabledReason = "FAILING" )
Disabled reasons.
type DuplicateOf ¶
type DuplicateOf struct {
RequestID string `json:"request_id"`
CheckedAt time.Time `json:"checked_at"`
}
DuplicateOf points at the earlier lookup of the same transfer.
type Endpoint ¶
type Endpoint struct {
ID string `json:"id"`
URL string `json:"url"`
Description *string `json:"description"`
EventTypes []EventType `json:"event_types"`
Enabled bool `json:"enabled"`
// DisabledReason says why Enabled is false, or is empty.
DisabledReason DisabledReason `json:"disabled_reason"`
// SecretHint is the end of the signing secret, to tell secrets apart.
SecretHint string `json:"secret_hint"`
// PreviousSecretExpiresAt is set during the 24 hours after a secret
// rotation, while the old secret still signs messages.
PreviousSecretExpiresAt *time.Time `json:"previous_secret_expires_at,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Endpoint is a URL of your system that receives webhook messages.
type EndpointWithSecret ¶
type EndpointWithSecret struct {
Endpoint
// Secret signs every message to this endpoint ("whsec_..."). It is
// returned only once, when the endpoint is created. Store it in your
// secret manager.
Secret string `json:"secret"`
}
EndpointWithSecret is a new endpoint together with its signing secret.
type Error ¶
type Error struct {
// StatusCode is the HTTP status, for example 422.
StatusCode int
// Code is the machine-readable error code, for example CodeInvalidQR.
Code ErrorCode
// Type is the problem type URI, a page that explains the error.
Type string
// Title is a short summary written for people. It may be in Thai.
Title string
// Detail explains this occurrence, when the API sends one.
Detail string
// Instance identifies this occurrence.
Instance string
// RequestID is the X-Request-Id of the response. Quote it when you
// contact Checkslip support.
RequestID string
// RetryAfter is the wait the API asked for in its Retry-After header,
// or zero.
RetryAfter time.Duration
// IdempotencyKey is the key the failed request carried, if any. Send
// the request again with the same key (WithIdempotencyKey) to get the
// first result instead of a second one.
IdempotencyKey string
// Fields lists the request fields that failed validation.
Fields []FieldError
// Body is the raw response body, kept for responses that are not
// problem details.
Body []byte
}
Error is returned for every API response with status 400 or above. The API sends errors as RFC 9457 problem details; Error holds their fields.
Use errors.As to reach it:
var apiErr *checkslip.Error
if errors.As(err, &apiErr) {
log.Println(apiErr.StatusCode, apiErr.Code, apiErr.RequestID)
}
Example ¶
package main
import (
"context"
"errors"
"fmt"
"net/http"
"net/http/httptest"
checkslip "github.com/Checkslip/sdk-go"
)
// fakeAPI stands in for api.checkslip.com so the examples run offline.
func fakeAPI() *httptest.Server {
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/api/slips/lookup":
w.Header().Set("Content-Type", "application/json")
fmt.Fprint(w, `{"request_id":"0192d4e0-7c1a-7b3e-9f00-1a2b3c4d5e6f","outcome":"FOUND","checked_at":"2026-10-29T10:41:02+07:00",
"slip":{"trans_ref":"630210534087","transferred_at":"2026-10-29T10:33:30+07:00","amount":{"satang":5000,"currency":"THB"},
"sender":{"bank":{"code":"KKP","bot_code":"069","name_th":null,"name_en":"Kiatnakin Phatra Bank"},"name_th":null,"name_en":"MR. SOMCHAI J","account":"0439"},
"receiver":{"kind":"BANK_ACCOUNT","bank":{"code":"GSB","bot_code":"030","name_th":null,"name_en":"Government Savings Bank"},"name_th":null,"name_en":"MISS SOMYING R","account":"9681"}}}`)
case "/api/billing/balance":
w.Header().Set("Content-Type", "application/problem+json")
w.Header().Set("X-Request-Id", "0192f3a4-7b1c-7d2e-8f30-4a5b6c7d8e9f")
w.WriteHeader(http.StatusForbidden)
fmt.Fprint(w, `{"type":"https://checkslip.com/problems/forbidden","title":"Forbidden","status":403,"code":"FORBIDDEN","instance":"/api/billing/balance"}`)
case "/api/slips":
w.Header().Set("Content-Type", "application/json")
if r.URL.Query().Get("cursor") == "" {
fmt.Fprint(w, `{"items":[{"request_id":"r1","outcome":"FOUND","checked_at":"2026-11-01T09:00:00Z","channel":"API"}],"next_cursor":"p2"}`)
return
}
fmt.Fprint(w, `{"items":[{"request_id":"r2","outcome":"NOT_FOUND","checked_at":"2026-11-01T08:00:00Z","channel":"API"}],"next_cursor":null}`)
default:
http.NotFound(w, r)
}
}))
}
func main() {
srv := fakeAPI()
defer srv.Close()
client := checkslip.NewClient("csk_example", checkslip.WithBaseURL(srv.URL))
_, err := client.Billing.Balance.Get(context.Background())
var apiErr *checkslip.Error
if errors.As(err, &apiErr) {
fmt.Println(apiErr.StatusCode, apiErr.Code, apiErr.RequestID)
}
fmt.Println(checkslip.IsForbidden(err))
}
Output: 403 FORBIDDEN 0192f3a4-7b1c-7d2e-8f30-4a5b6c7d8e9f true
type ErrorCode ¶
type ErrorCode string
ErrorCode is the machine-readable code of an API error, such as "RATE_LIMITED". Compare it with the Code constants. New codes may appear over time, so treat an unknown code like a generic failure of its status.
const ( CodeValidationFailed ErrorCode = "VALIDATION_FAILED" CodeInvalidCursor ErrorCode = "INVALID_CURSOR" CodeUnauthenticated ErrorCode = "UNAUTHENTICATED" CodeForbidden ErrorCode = "FORBIDDEN" CodeRateLimited ErrorCode = "RATE_LIMITED" CodeInternal ErrorCode = "INTERNAL" CodeIdempotencyKeyRequired ErrorCode = "IDEMPOTENCY_KEY_REQUIRED" CodeIdempotencyKeyInvalid ErrorCode = "IDEMPOTENCY_KEY_INVALID" CodeIdempotencyKeyReused ErrorCode = "IDEMPOTENCY_KEY_REUSED" CodeIdempotencyInFlight ErrorCode = "IDEMPOTENCY_IN_FLIGHT" // Slip lookup. CodeNoCredit ErrorCode = "NO_CREDIT" CodeCreditExpired ErrorCode = "CREDIT_EXPIRED" CodeInvalidQR ErrorCode = "INVALID_QR" CodeQRTampered ErrorCode = "QR_TAMPERED" CodeTooOld ErrorCode = "TOO_OLD" CodeInvalidFile ErrorCode = "INVALID_FILE" CodeMultipleSlipQR ErrorCode = "MULTIPLE_SLIP_QR" CodePayloadTooLarge ErrorCode = "PAYLOAD_TOO_LARGE" CodeUnsupportedMediaType ErrorCode = "UNSUPPORTED_MEDIA_TYPE" // Billing. CodeOrderNotFound ErrorCode = "ORDER_NOT_FOUND" CodeReceiptNotFound ErrorCode = "RECEIPT_NOT_FOUND" // Webhooks. CodeEndpointNotFound ErrorCode = "ENDPOINT_NOT_FOUND" CodeDeliveryNotFound ErrorCode = "DELIVERY_NOT_FOUND" CodeEndpointLimitReached ErrorCode = "ENDPOINT_LIMIT_REACHED" CodeEndpointDisabled ErrorCode = "ENDPOINT_DISABLED" CodeEndpointDeleted ErrorCode = "ENDPOINT_DELETED" CodeRotationInProgress ErrorCode = "ROTATION_IN_PROGRESS" CodePayloadExpired ErrorCode = "PAYLOAD_EXPIRED" CodeURLNotHTTPS ErrorCode = "URL_NOT_HTTPS" CodeURLHasUserinfo ErrorCode = "URL_HAS_USERINFO" CodeDestinationBlocked ErrorCode = "DESTINATION_BLOCKED" CodeDNSUnresolvable ErrorCode = "DNS_UNRESOLVABLE" CodeUnknownEventType ErrorCode = "UNKNOWN_EVENT_TYPE" CodeRecoverWindowInvalid ErrorCode = "RECOVER_WINDOW_INVALID" CodePayloadIntegrityMismatch ErrorCode = "PAYLOAD_INTEGRITY_MISMATCH" )
Error codes the Checkslip API returns. The list covers the codes a customer is likely to handle; Error.Code can hold others.
type EventTypeInfo ¶
type EventTypeInfo struct {
Type EventType `json:"type"`
Description string `json:"description"`
// Subscribable reports whether the type can be listed in an
// endpoint's EventTypes.
Subscribable bool `json:"subscribable"`
}
EventTypeInfo describes one webhook event type.
type EvidenceExport ¶
type EvidenceExport struct {
ExportID string `json:"export_id"`
State ExportState `json:"status"`
FromDay Date `json:"from_day"`
ToDay Date `json:"to_day"`
Streams []Stream `json:"streams"`
CreatedAt time.Time `json:"created_at"`
CreatedBy *ExportCreator `json:"created_by,omitempty"`
ReadyAt *time.Time `json:"ready_at,omitempty"`
// ExpiresAt is when the file is deleted. Download it before then.
ExpiresAt *time.Time `json:"expires_at,omitempty"`
Manifest *ExportManifest `json:"manifest,omitempty"`
}
EvidenceExport is an evidence file of the account.
type EvidenceFile ¶
type EvidenceFile struct {
io.ReadCloser
// ReprDigest is the Repr-Digest header, the file's SHA-256.
ReprDigest string
// Size is the Content-Length, or -1 when unknown.
Size int64
}
EvidenceFile is a downloaded evidence file. Read it as NDJSON, one record per line; the last line is the manifest. Close it when done.
type ExportColumn ¶
type ExportColumn string
ExportColumn is one column of a lookup history CSV.
latency_ms, upstream_ms and checked_elsewhere are deliberately absent: they left the Checkslip API, so they are not offered as export columns.
const ( ColumnRequestID ExportColumn = "request_id" ColumnOutcome ExportColumn = "outcome" ColumnStatus ExportColumn = "status" ColumnCheckedAt ExportColumn = "checked_at" ColumnChannel ExportColumn = "channel" ColumnAPIKeyID ExportColumn = "api_key_id" ColumnClientReference ExportColumn = "client_reference" ColumnDuplicateOfRequestID ExportColumn = "duplicate_of.request_id" ColumnChargeConfirmed ExportColumn = "charge_confirmed" ColumnCharged ExportColumn = "charged" ColumnFreeReason ExportColumn = "free_reason" ColumnSlipTransRef ExportColumn = "slip.trans_ref" ColumnSlipTransferredAt ExportColumn = "slip.transferred_at" ColumnSlipAmountSatang ExportColumn = "slip.amount.satang" ColumnSlipAmountCurrency ExportColumn = "slip.amount.currency" ColumnSlipSenderBankCode ExportColumn = "slip.sender.bank.code" ColumnSlipSenderBankBOTCode ExportColumn = "slip.sender.bank.bot_code" ColumnSlipSenderNameTH ExportColumn = "slip.sender.name_th" ColumnSlipSenderNameEN ExportColumn = "slip.sender.name_en" ColumnSlipSenderAccount ExportColumn = "slip.sender.account" ColumnSlipReceiverBankCode ExportColumn = "slip.receiver.bank.code" ColumnSlipReceiverBankBOTCode ExportColumn = "slip.receiver.bank.bot_code" ColumnSlipReceiverNameTH ExportColumn = "slip.receiver.name_th" ColumnSlipReceiverNameEN ExportColumn = "slip.receiver.name_en" ColumnSlipReceiverAccount ExportColumn = "slip.receiver.account" )
Export columns.
type ExportCreator ¶
ExportCreator is who asked for an evidence file.
type ExportManifest ¶
type ExportManifest struct {
// Format is "checkslip-audit-export/3".
Format string `json:"format"`
// RecordCount is how many records the file holds.
RecordCount int64 `json:"record_count"`
// SHA256 covers every byte of the file before its last line.
SHA256 string `json:"sha256"`
SizeBytes int64 `json:"size_bytes"`
}
ExportManifest describes a READY evidence file.
type ExportMode ¶
type ExportMode string
ExportMode says whether a lookup history export came back at once or runs as a background job.
const ( ExportDirect ExportMode = "DIRECT" ExportJob ExportMode = "JOB" )
Export modes.
type ExportPart ¶
type ExportPart struct {
Part int `json:"part"`
Rows *int64 `json:"rows,omitempty"`
// URL is a signed download link. Pass the part to DownloadExportPart.
URL string `json:"url"`
URLExpiresAt time.Time `json:"url_expires_at"`
}
ExportPart is one downloadable CSV file of an export.
type ExportState ¶
type ExportState string
ExportState is the state of a lookup history export or an evidence file.
const ( ExportQueued ExportState = "QUEUED" ExportRunning ExportState = "RUNNING" ExportReady ExportState = "READY" ExportFailed ExportState = "FAILED" // ExportExpired means the file was deleted. ExportExpired ExportState = "EXPIRED" )
Export states.
type FieldError ¶
type FieldError struct {
// Pointer is a JSON Pointer to the field, for example "/qr".
Pointer string `json:"pointer"`
// Code says what is wrong with the field.
Code string `json:"code"`
// Detail explains it for people, when sent.
Detail string `json:"detail,omitempty"`
}
FieldError points at one request field that failed validation.
type Fingerprint ¶
type Fingerprint struct {
QRSHA256 string `json:"qr_sha256,omitempty"`
TransRef string `json:"trans_ref,omitempty"`
}
Fingerprint identifies the slip a request carried without storing it.
type FreeReason ¶
type FreeReason string
FreeReason says why a lookup used no credit.
A customer receives NOT_FOUND, DUPLICATE, UNAVAILABLE, INVALID_QR, INVALID_FILE, QR_TAMPERED, TOO_OLD, SYSTEM_ERROR and OTHER. The other values exist inside the API and do not reach you.
const ( FreeReasonNotFound FreeReason = "NOT_FOUND" FreeReasonDuplicate FreeReason = "DUPLICATE" FreeReasonPending FreeReason = "PENDING" FreeReasonInvalidQR FreeReason = "INVALID_QR" FreeReasonInvalidFile FreeReason = "INVALID_FILE" FreeReasonQRTampered FreeReason = "QR_TAMPERED" FreeReasonTooOld FreeReason = "TOO_OLD" // FreeReasonNoCredit and FreeReasonHoldExpired are no longer sent. The // API sends FreeReasonOther in their place. FreeReasonNoCredit FreeReason = "NO_CREDIT" FreeReasonHoldExpired FreeReason = "HOLD_EXPIRED" FreeReasonSystemError FreeReason = "SYSTEM_ERROR" // FreeReasonOther means the lookup was not charged and no reason was // given. FreeReasonOther FreeReason = "OTHER" )
Free reasons.
type Iter ¶
type Iter[T any] struct { // contains filtered or unexported fields }
Iter walks every item of a list across pages, fetching the next page with next_cursor as it goes. Get one from a ListAll method:
it := client.Slips.ListAll(ctx, &checkslip.ListSlipsParams{})
for it.Next() {
rec := it.Current()
_ = rec
}
if err := it.Err(); err != nil {
return err
}
With Go 1.23 or later you can also range over Iter.All.
An Iter is not safe for concurrent use.
Example ¶
package main
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
checkslip "github.com/Checkslip/sdk-go"
)
// fakeAPI stands in for api.checkslip.com so the examples run offline.
func fakeAPI() *httptest.Server {
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/api/slips/lookup":
w.Header().Set("Content-Type", "application/json")
fmt.Fprint(w, `{"request_id":"0192d4e0-7c1a-7b3e-9f00-1a2b3c4d5e6f","outcome":"FOUND","checked_at":"2026-10-29T10:41:02+07:00",
"slip":{"trans_ref":"630210534087","transferred_at":"2026-10-29T10:33:30+07:00","amount":{"satang":5000,"currency":"THB"},
"sender":{"bank":{"code":"KKP","bot_code":"069","name_th":null,"name_en":"Kiatnakin Phatra Bank"},"name_th":null,"name_en":"MR. SOMCHAI J","account":"0439"},
"receiver":{"kind":"BANK_ACCOUNT","bank":{"code":"GSB","bot_code":"030","name_th":null,"name_en":"Government Savings Bank"},"name_th":null,"name_en":"MISS SOMYING R","account":"9681"}}}`)
case "/api/billing/balance":
w.Header().Set("Content-Type", "application/problem+json")
w.Header().Set("X-Request-Id", "0192f3a4-7b1c-7d2e-8f30-4a5b6c7d8e9f")
w.WriteHeader(http.StatusForbidden)
fmt.Fprint(w, `{"type":"https://checkslip.com/problems/forbidden","title":"Forbidden","status":403,"code":"FORBIDDEN","instance":"/api/billing/balance"}`)
case "/api/slips":
w.Header().Set("Content-Type", "application/json")
if r.URL.Query().Get("cursor") == "" {
fmt.Fprint(w, `{"items":[{"request_id":"r1","outcome":"FOUND","checked_at":"2026-11-01T09:00:00Z","channel":"API"}],"next_cursor":"p2"}`)
return
}
fmt.Fprint(w, `{"items":[{"request_id":"r2","outcome":"NOT_FOUND","checked_at":"2026-11-01T08:00:00Z","channel":"API"}],"next_cursor":null}`)
default:
http.NotFound(w, r)
}
}))
}
func main() {
srv := fakeAPI()
defer srv.Close()
client := checkslip.NewClient("csk_example", checkslip.WithBaseURL(srv.URL))
it := client.Slips.ListAll(context.Background(), &checkslip.ListSlipsParams{
ListOptions: checkslip.ListOptions{Limit: 100},
})
for it.Next() {
rec := it.Current()
fmt.Println(rec.RequestID, rec.Outcome)
}
if err := it.Err(); err != nil {
fmt.Println(err)
}
}
Output: r1 FOUND r2 NOT_FOUND
func (*Iter[T]) All ¶
All returns the remaining items as an iterator for range loops. The loop gets a non-nil error at most once, as its last value; stop on it.
for rec, err := range client.Slips.ListAll(ctx, nil).All() {
if err != nil {
return err
}
fmt.Println(rec.RequestID)
}
All needs Go 1.23 or later. On Go 1.22 use Next, Current and Err.
Example ¶
srv := fakeAPI()
defer srv.Close()
client := checkslip.NewClient("csk_example", checkslip.WithBaseURL(srv.URL))
for rec, err := range client.Slips.ListAll(context.Background(), nil).All() {
if err != nil {
fmt.Println(err)
return
}
fmt.Println(rec.RequestID)
}
Output: r1 r2
func (*Iter[T]) Cursor ¶
Cursor returns the cursor of the page after the one being read. Save it to resume a long walk later with ListOptions.Cursor. It is empty once the last page has been fetched.
type LedgerParams ¶
type LedgerParams struct {
ListOptions
// Reason keeps only these reasons.
Reason []CreditReason
// From and To limit the time range. Zero means no limit.
From, To time.Time
}
LedgerParams filters the credit ledger.
type ListAuditEventsParams ¶
type ListAuditEventsParams struct {
ListOptions
From, To time.Time
// Action is a full action name, or a group ending in ".*" such as
// "api_key.*".
Action string
ActorType ActorType
ActorID string
Channel Channel
TargetType string
TargetID string
}
ListAuditEventsParams filters the account activity. Without From and To the API returns the last 24 hours; one call covers at most 31 days.
type ListDeliveriesParams ¶
type ListDeliveriesParams struct {
ListOptions
EndpointID string
Status DeliveryStatus
EventType EventType
// Since and Until limit the time range. Zero means no limit.
Since, Until time.Time
}
ListDeliveriesParams filters the delivery list.
type ListOptions ¶
type ListOptions struct {
// Limit is the page size, 1 to 100. Zero uses the API default of 20.
Limit int
// Cursor is the NextCursor of the previous page. Leave it empty for the
// first page.
Cursor string
}
ListOptions holds the paging fields every list call accepts.
type ListRequestsParams ¶
type ListRequestsParams struct {
ListOptions
From, To time.Time
// HTTPStatus, OutcomeCode, Route and Channel each take several values,
// and a record matches when it matches any one of them. The API allows
// at most 20 statuses, codes or routes, and at most 5 channels.
HTTPStatus []int
OutcomeCode []string
Route []string
Channel []Channel
APIKeyID string
// TransRef or QRSHA256 find whether a slip was sent before.
TransRef string
QRSHA256 string
}
ListRequestsParams filters the request records. Without From and To the API returns the last 24 hours; one call covers at most 31 days.
type ListSlipsParams ¶
type ListSlipsParams struct {
ListOptions
// Q is an exact match (never partial) on a request ID, transfer
// reference or client reference. It ignores From and To.
Q string
// TransRef finds lookups of one transfer reference, for example to see
// whether a slip was sent before.
TransRef string
// Outcome keeps only these outcomes.
Outcome []Outcome
// OutcomeNot drops these outcomes.
OutcomeNot []Outcome
// SenderBank is the three-digit Bank of Thailand code of the sender.
SenderBank string
// APIKey is the 12-character ID of the API key that made the lookup.
APIKey string
// After returns only lookups newer than this request ID. Do not
// combine it with a cursor.
After string
// Charged filters by whether a credit was used.
Charged []ChargedFilter
// FreeReason keeps lookups that used no credit for these reasons.
FreeReason []FreeReason
// From and To limit the time range. Zero means no limit.
From, To time.Time
}
ListSlipsParams filters the lookup history. Every field is optional.
type LookupParams ¶
type LookupParams struct {
// QR is the text of the QR code printed on the slip.
QR string `json:"qr,omitempty"`
// Image is the slip picture instead of its QR text: JPEG, PNG or WebP,
// at most 10 MB and 50 megapixels. The API reads the QR from the image
// and does not store the image. The client reads it fully into memory so
// the request can be sent again on a retry.
Image io.Reader `json:"-"`
// Filename is sent as the file name of an image upload. Optional.
Filename string `json:"-"`
// ContentType is the image type, such as "image/png". When empty the
// client detects it from the first bytes.
ContentType string `json:"-"`
// ClientReference is your own reference for this lookup, such as an
// order number. It comes back in the answer, the history and webhooks.
ClientReference string `json:"client_reference,omitempty"`
}
LookupParams is the input of SlipsService.Lookup.
Set exactly one of QR or Image. Setting both, or neither, is an error the client reports before it sends anything.
type Money ¶
type Money struct {
// Satang is the amount in satang (1/100 baht).
Satang int64 `json:"satang"`
// Currency is the ISO 4217 code, "THB" for every slip today.
Currency string `json:"currency"`
}
Money is an amount in the smallest unit of its currency. For THB that is satang: 5000 satang is 50.00 baht.
type Option ¶
type Option func(*Client)
Option configures a Client. Pass options to NewClient.
func WithBaseURL ¶
WithBaseURL sets the API address, for example a local mock server in tests. The default is DefaultBaseURL. Plain http is accepted only for loopback hosts such as 127.0.0.1 and localhost, so an API key is never sent in clear text over a network.
func WithHTTPClient ¶
WithHTTPClient sets the http.Client used to send requests. Use it to add a proxy, custom TLS settings or instrumentation. The client's own Timeout, if any, applies on top of the per-attempt timeout from WithTimeout.
func WithMaxRetries ¶
WithMaxRetries sets how many times a failed request is sent again. The default is 2, so a request is sent at most 3 times. Zero turns retries off. See the package documentation for which failures are retried.
func WithTimeout ¶
WithTimeout sets how long a single attempt may take, including reading the response body. Retries get a fresh timeout each. The default is 30 seconds. For downloads (DownloadExport, DownloadExportPart) it covers the wait for the response headers only; the context limits reading the file. Zero or a negative value turns the per-attempt timeout off; the context passed to each method still applies.
func WithUserAgent ¶
WithUserAgent puts your application's name and version in front of the SDK's own User-Agent, for example "my-shop/1.4". It helps Checkslip support find your requests when you ask about them.
type Order ¶
type Order struct {
ID string `json:"id"`
Status OrderStatus `json:"status"`
PackageCode string `json:"package_code"`
PackageNameTH string `json:"package_name_th"`
Credits int64 `json:"credits"`
Amount Money `json:"amount"`
PaymentMethod PaymentMethod `json:"payment_method,omitempty"`
ReceiptID *string `json:"receipt_id,omitempty"`
CreatedAt time.Time `json:"created_at"`
PaidAt *time.Time `json:"paid_at,omitempty"`
CancelledAt *time.Time `json:"cancelled_at,omitempty"`
// Confirming is true when the payment page was closed but the money
// has not arrived yet.
Confirming bool `json:"confirming,omitempty"`
ReviewReason *string `json:"review_reason,omitempty"`
}
Order is a purchase of credits.
type OrderStatus ¶
type OrderStatus string
OrderStatus is the state of a credit order.
const ( OrderPending OrderStatus = "PENDING" OrderPaid OrderStatus = "PAID" OrderCancelled OrderStatus = "CANCELLED" OrderFlagged OrderStatus = "FLAGGED" )
Order statuses.
type OrdersParams ¶
type OrdersParams struct {
ListOptions
Status OrderStatus
}
OrdersParams filters the order list.
type Outcome ¶
type Outcome string
Outcome is the result of one slip lookup.
const ( // OutcomeFound means the sending bank returned the transfer. This is // the only outcome that uses a credit. OutcomeFound Outcome = "FOUND" // OutcomeNotFound means the bank has no transfer for this reference. // Whether to accept the payment is your decision. OutcomeNotFound Outcome = "NOT_FOUND" // OutcomeDuplicate means this account looked up the same transfer // before; DuplicateOf points at the first lookup. OutcomeDuplicate Outcome = "DUPLICATE" // OutcomePending means the bank does not show the transfer yet. Read // the result later with Slips.Get or wait for the slip.checked webhook. OutcomePending Outcome = "PENDING" OutcomeUnavailable Outcome = "UNAVAILABLE" // OutcomeInvalidQR means the QR text could not be read as a slip QR. OutcomeInvalidQR Outcome = "INVALID_QR" // OutcomeInvalidFile means the image could not be read. OutcomeInvalidFile Outcome = "INVALID_FILE" // OutcomeQRTampered means the QR text failed its integrity check. OutcomeQRTampered Outcome = "QR_TAMPERED" // OutcomeTooOld means the transfer is older than the bank lets us look // back (see SupportedBank.LookbackDays). OutcomeTooOld Outcome = "TOO_OLD" )
Outcomes. Lookup returns FOUND, NOT_FOUND, DUPLICATE or PENDING; the other values appear in the lookup history and in webhooks for lookups the API answered with an error.
type Package ¶
type Package struct {
Code string `json:"code"`
NameTH string `json:"name_th"`
NameEN string `json:"name_en"`
Price Money `json:"price"`
PricePerCredit Money `json:"price_per_credit"`
Credits int64 `json:"credits"`
// ValidityDays is how long the credits last. Credits from every
// package join one balance that lasts from the latest purchase.
ValidityDays int `json:"validity_days"`
ForSale bool `json:"for_sale"`
Sort int `json:"sort"`
}
Package is a credit package for sale.
type PackageList ¶
type PackageList struct {
// CheckoutOpen reports whether packages can be bought right now.
CheckoutOpen bool `json:"checkout_open"`
PricesIncludeVAT bool `json:"prices_include_vat"`
Items []Package `json:"items"`
}
PackageList is the list of credit packages.
type Page ¶
Page is one page of a list. NextCursor is empty on the last page.
func (*Page[T]) UnmarshalJSON ¶
UnmarshalJSON implements json.Unmarshaler.
type Party ¶
type Party struct {
Bank Bank `json:"bank"`
// NameTH and NameEN are the names the bank holds, often shortened by
// the bank. Either may be nil.
NameTH *string `json:"name_th"`
NameEN *string `json:"name_en"`
// Account is the part of the account number the bank shows, or nil.
Account *string `json:"account"`
}
Party is the sender of a transfer as the bank returned it.
type PaymentMethod ¶
type PaymentMethod string
PaymentMethod is how an order was paid.
const ( PaymentCard PaymentMethod = "CARD" PaymentPromptPay PaymentMethod = "PROMPTPAY" )
Payment methods.
type Receipt ¶
type Receipt struct {
ID string `json:"id"`
// SellerReady reports whether the seller details are complete.
SellerReady bool `json:"seller_ready"`
ReceiptNo string `json:"receipt_no"`
OrderID string `json:"order_id"`
IssuedAt time.Time `json:"issued_at"`
Seller ReceiptSeller `json:"seller"`
Buyer BillingInfo `json:"buyer"`
Lines []ReceiptLine `json:"lines"`
VAT *ReceiptVAT `json:"vat,omitempty"`
Total Money `json:"total"`
PaymentMethod PaymentMethod `json:"payment_method"`
}
Receipt is a tax receipt for a paid order.
type ReceiptLine ¶
type ReceiptLine struct {
Description string `json:"description"`
Quantity int `json:"quantity"`
Amount Money `json:"amount"`
}
ReceiptLine is one line of a receipt.
type ReceiptSeller ¶
type ReceiptSeller struct {
Name string `json:"name"`
TaxID string `json:"tax_id,omitempty"`
Address string `json:"address,omitempty"`
Branch string `json:"branch,omitempty"`
}
ReceiptSeller is the seller printed on a receipt.
type ReceiptVAT ¶
type ReceiptVAT struct {
// RateBP is the rate in basis points: 700 is 7%.
RateBP int `json:"rate_bp"`
Base Money `json:"base"`
Amount Money `json:"amount"`
}
ReceiptVAT is the VAT on a receipt.
type Receiver ¶
type Receiver struct {
Kind ReceiverKind `json:"kind"`
// Bank is nil for some bill payments.
Bank *Bank `json:"bank"`
NameTH *string `json:"name_th"`
NameEN *string `json:"name_en"`
Account *string `json:"account"`
}
Receiver is the receiving side of a transfer.
type ReceiverKind ¶
type ReceiverKind string
ReceiverKind says what kind of account received a transfer.
const ( ReceiverBankAccount ReceiverKind = "BANK_ACCOUNT" ReceiverBillPayment ReceiverKind = "BILL_PAYMENT" ReceiverNotBankAccount ReceiverKind = "NOT_BANK_ACCOUNT" )
Receiver kinds.
type RecoverParams ¶
type RecoverParams struct {
// Since is the start of the range. Required.
Since time.Time `json:"since"`
// Until is the end, not included. Zero means now.
Until time.Time `json:"-"`
}
RecoverParams is the time range of WebhookEndpointsService.Recover.
func (RecoverParams) MarshalJSON ¶
func (p RecoverParams) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler.
type RecoverResult ¶
type RecoverResult struct {
EndpointID string `json:"endpoint_id"`
Since time.Time `json:"since"`
Until time.Time `json:"until"`
}
RecoverResult confirms a recover request.
type RequestOption ¶
type RequestOption func(*requestConfig)
RequestOption changes a single API call. Pass it as the last argument of any service method.
func WithHeader ¶
func WithHeader(key, value string) RequestOption
WithHeader adds an HTTP header to this call, for example a W3C traceparent header. It cannot change Authorization, Content-Type or Idempotency-Key.
func WithIdempotencyKey ¶
func WithIdempotencyKey(key string) RequestOption
WithIdempotencyKey sends the Idempotency-Key header with this call. A call sent again with the same key returns the first result and is not processed twice; a lookup is never charged twice.
Methods that need a key generate a random one when you do not pass one. Pass your own, for example derived from your order ID, when you may resend the same request after a restart of your program.
Example ¶
package main
import (
"context"
"fmt"
"os"
checkslip "github.com/Checkslip/sdk-go"
)
func main() {
client := checkslip.NewClient(os.Getenv("CHECKSLIP_API_KEY"))
// A key tied to your order lets you resend after a crash and get the
// first answer back, with no second charge.
res, err := client.Slips.Lookup(context.Background(),
&checkslip.LookupParams{QR: "..."},
checkslip.WithIdempotencyKey("order-1001-slip-1"),
)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(res.Outcome)
}
Output:
type SecretRotation ¶
type SecretRotation struct {
// Secret is the new signing secret, returned only once.
Secret string `json:"secret"`
// PreviousSecretExpiresAt is when the old secret stops signing. Until
// then every message carries both signatures.
PreviousSecretExpiresAt time.Time `json:"previous_secret_expires_at"`
}
SecretRotation is the result of WebhookEndpointsService.RotateSecret.
type Slip ¶
type Slip struct {
// TransRef is the transfer reference printed on the slip.
TransRef string `json:"trans_ref"`
// TransferredAt is when the bank says the money moved. It is the zero
// time on summaries that do not carry it.
TransferredAt time.Time `json:"transferred_at"`
// Amount is the transferred amount. It is nil on summaries that do not
// carry it.
Amount *Money `json:"amount,omitempty"`
// Sender and Receiver are set on a full slip.
Sender *Party `json:"sender,omitempty"`
Receiver *Receiver `json:"receiver,omitempty"`
// NamesErased is true when the names and masked accounts of this
// transfer were erased together with the account's data. The name and
// account fields are then nil because Checkslip no longer has them. A
// nil name without NamesErased means the bank did not report it.
NamesErased bool `json:"names_erased,omitempty"`
// SenderBank and ReceiverBank are set on a summary.
SenderBank *Bank `json:"sender_bank,omitempty"`
ReceiverBank *Bank `json:"receiver_bank,omitempty"`
}
Slip is the transfer data returned with a lookup.
A FOUND lookup carries the full slip: Sender and Receiver are set. Other outcomes and some history rows carry a summary: SenderBank (and maybe ReceiverBank) are set and Sender and Receiver are nil.
type SlipCheck ¶
type SlipCheck struct {
// RequestID identifies this lookup. Read it again with Slips.Get.
RequestID string `json:"request_id"`
// Outcome is FOUND, NOT_FOUND, DUPLICATE or PENDING.
Outcome Outcome `json:"outcome"`
CheckedAt time.Time `json:"checked_at"`
// latency_ms, upstream_ms and checked_elsewhere left the Checkslip API,
// so this client does not carry them.
// Slip is the transfer data. It may be a summary; see Slip.
Slip *Slip `json:"slip"`
// DuplicateOf is set when Outcome is DUPLICATE.
DuplicateOf *DuplicateOf `json:"duplicate_of,omitempty"`
// ClientReference echoes the reference you sent, if any.
ClientReference *string `json:"client_reference,omitempty"`
// IdempotencyKey is the Idempotency-Key the request carried. Send the
// same lookup again with this key to get this same answer.
IdempotencyKey string `json:"-"`
// RetryAfter is set on a PENDING answer: the API's suggested wait
// before reading the result again with Slips.Get.
RetryAfter time.Duration `json:"-"`
}
SlipCheck is the answer to Slips.Lookup.
The API returns what the sending bank holds. Whether the payment is acceptable for your order is your decision: compare at least Amount, Receiver and TransferredAt with what you expected.
type SlipCheckRecord ¶
type SlipCheckRecord struct {
RequestID string `json:"request_id"`
Outcome Outcome `json:"outcome"`
CheckedAt time.Time `json:"checked_at"`
// APIKeyID is the key that made the lookup, or nil for lookups made in
// the web app.
APIKeyID *string `json:"api_key_id,omitempty"`
// Status is the HTTP status the lookup was answered with.
Status int `json:"status,omitempty"`
// latency_ms, upstream_ms and checked_elsewhere left the Checkslip API,
// so this client does not carry them.
Channel Channel `json:"channel"`
Slip *Slip `json:"slip,omitempty"`
DuplicateOf *DuplicateOf `json:"duplicate_of,omitempty"`
ClientReference *string `json:"client_reference,omitempty"`
// Charged is true when the lookup used a credit, false when it did
// not, and nil while that is not settled yet.
Charged *bool `json:"charged,omitempty"`
// ChargeConfirmed is nil until the charge is settled.
ChargeConfirmed *bool `json:"charge_confirmed,omitempty"`
// FreeReason says why no credit was used, when Charged is false.
FreeReason *FreeReason `json:"free_reason,omitempty"`
}
SlipCheckRecord is one row of the lookup history.
type SlipCheckedData ¶
type SlipCheckedData struct {
RequestID string `json:"request_id"`
Outcome Outcome `json:"outcome"`
CheckedAt time.Time `json:"checked_at"`
// Channel is API, DASHBOARD or ASSISTANT.
Channel Channel `json:"channel"`
ClientReference *string `json:"client_reference"`
Slip *Slip `json:"slip,omitempty"`
DuplicateOf *DuplicateOf `json:"duplicate_of,omitempty"`
// ResolvedAfterPending is true when this lookup was PENDING first.
ResolvedAfterPending bool `json:"resolved_after_pending"`
}
SlipCheckedData is the data of a slip.checked message.
type SlipCounts ¶
type SlipCounts struct {
Bucket CountBucket `json:"bucket"`
// AsOf is the time the counts were computed.
AsOf time.Time `json:"as_of"`
// FiltersApplied lists the filters the counts honor.
FiltersApplied []string `json:"filters_applied"`
// FiltersIgnored lists filters that were sent but do not apply to
// counts.
FiltersIgnored []string `json:"filters_ignored"`
Series []CountSeries `json:"series"`
}
SlipCounts is the number of lookups per time bucket.
type SlipCountsParams ¶
type SlipCountsParams struct {
// From and To are the time range. Required.
From, To time.Time
// Bucket is the size of each count. Required.
Bucket CountBucket
// Outcome and OutcomeNot filter by outcome.
Outcome []Outcome
OutcomeNot []Outcome
}
SlipCountsParams is the input of SlipsService.Counts.
type SlipExport ¶
type SlipExport struct {
ExportID string `json:"export_id"`
State ExportState `json:"state"`
Mode ExportMode `json:"mode,omitempty"`
CreatedAt time.Time `json:"created_at"`
Request SlipExportParams `json:"request"`
// Rows is the number of rows, once known.
Rows *int64 `json:"rows,omitempty"`
// Truncated is true when the export hit its row limit. Create another
// export with Cursor set to ContinueCursor for the rest.
Truncated bool `json:"truncated,omitempty"`
ContinueCursor *string `json:"continue_cursor,omitempty"`
// ExpiresAt is when the parts are deleted.
ExpiresAt *time.Time `json:"expires_at,omitempty"`
Parts []ExportPart `json:"parts,omitempty"`
}
SlipExport is a CSV export of the lookup history.
type SlipExportParams ¶
type SlipExportParams struct {
// Columns lists the CSV columns in order. Required.
Columns []ExportColumn `json:"columns"`
// Q, TransRef and the other filters work as in ListSlipsParams.
Q string `json:"q,omitempty"`
TransRef string `json:"trans_ref,omitempty"`
Outcome []Outcome `json:"outcome,omitempty"`
OutcomeNot []Outcome `json:"outcome_not,omitempty"`
SenderBank string `json:"sender_bank,omitempty"`
APIKey string `json:"api_key,omitempty"`
Charged []ChargedFilter `json:"charged,omitempty"`
FreeReason []FreeReason `json:"free_reason,omitempty"`
From *time.Time `json:"from,omitempty"`
To *time.Time `json:"to,omitempty"`
// Cursor continues a truncated export: pass SlipExport.ContinueCursor.
Cursor string `json:"cursor,omitempty"`
}
SlipExportParams describes a CSV export of the lookup history.
type SlipExportsService ¶
type SlipExportsService struct {
// contains filtered or unexported fields
}
SlipExportsService builds and downloads CSV files of past lookups.
func (*SlipExportsService) Create ¶
func (s *SlipExportsService) Create(ctx context.Context, params *SlipExportParams, opts ...RequestOption) (*SlipExport, error)
Create starts a CSV export of the lookup history. Up to 1,000,000 rows come back at once (Mode DIRECT); more runs as a background job (Mode JOB): poll GetExport until State is READY. One export runs per account at a time. The file holds payer and payee names, so store it with care.
func (*SlipExportsService) DownloadPart ¶
func (s *SlipExportsService) DownloadPart(ctx context.Context, part ExportPart, opts ...RequestOption) (io.ReadCloser, error)
DownloadPart downloads one CSV part of a READY export. The caller must close the returned body. The part's URL must point at the API host the client uses; the client refuses other hosts so the API key stays with Checkslip.
func (*SlipExportsService) Get ¶
func (s *SlipExportsService) Get(ctx context.Context, exportID string, opts ...RequestOption) (*SlipExport, error)
Get returns the state of a lookup history export.
type SlipsService ¶
type SlipsService struct {
// Exports builds CSV files of past lookups.
Exports *SlipExportsService
// contains filtered or unexported fields
}
SlipsService looks up slips and reads the lookup history.
func (*SlipsService) Counts ¶
func (s *SlipsService) Counts(ctx context.Context, params *SlipCountsParams, opts ...RequestOption) (*SlipCounts, error)
Counts returns how many lookups were made per time bucket, split by outcome.
func (*SlipsService) Get ¶
func (s *SlipsService) Get(ctx context.Context, requestID string, opts ...RequestOption) (*SlipCheckRecord, error)
Get returns one lookup of this account by its request ID. Use it to read the result of a PENDING lookup later. An unknown ID and another account's ID both return an error for which IsNotFound is true.
func (*SlipsService) List ¶
func (s *SlipsService) List(ctx context.Context, params *ListSlipsParams, opts ...RequestOption) (*Page[SlipCheckRecord], error)
List returns one page of the lookup history, newest first. FOUND rows carry the payer and payee names with masked account numbers.
func (*SlipsService) ListAll ¶
func (s *SlipsService) ListAll(ctx context.Context, params *ListSlipsParams, opts ...RequestOption) *Iter[SlipCheckRecord]
ListAll walks the whole lookup history that matches params, page by page.
func (*SlipsService) Lookup ¶
func (s *SlipsService) Lookup(ctx context.Context, params *LookupParams, opts ...RequestOption) (*SlipCheck, error)
Lookup asks the sending bank for the transfer a slip refers to. Pass either the QR text of the slip in QR, or the slip image in Image.
The answer's Outcome is FOUND, NOT_FOUND, DUPLICATE or PENDING. Only FOUND uses a credit. A QR the API cannot use comes back as an *Error with a code such as CodeInvalidQR, CodeQRTampered or CodeTooOld, and uses no credit.
Lookup always sends an Idempotency-Key, generating one unless you pass WithIdempotencyKey, so retries never charge twice. The key used is in SlipCheck.IdempotencyKey.
Example ¶
package main
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
checkslip "github.com/Checkslip/sdk-go"
)
// fakeAPI stands in for api.checkslip.com so the examples run offline.
func fakeAPI() *httptest.Server {
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/api/slips/lookup":
w.Header().Set("Content-Type", "application/json")
fmt.Fprint(w, `{"request_id":"0192d4e0-7c1a-7b3e-9f00-1a2b3c4d5e6f","outcome":"FOUND","checked_at":"2026-10-29T10:41:02+07:00",
"slip":{"trans_ref":"630210534087","transferred_at":"2026-10-29T10:33:30+07:00","amount":{"satang":5000,"currency":"THB"},
"sender":{"bank":{"code":"KKP","bot_code":"069","name_th":null,"name_en":"Kiatnakin Phatra Bank"},"name_th":null,"name_en":"MR. SOMCHAI J","account":"0439"},
"receiver":{"kind":"BANK_ACCOUNT","bank":{"code":"GSB","bot_code":"030","name_th":null,"name_en":"Government Savings Bank"},"name_th":null,"name_en":"MISS SOMYING R","account":"9681"}}}`)
case "/api/billing/balance":
w.Header().Set("Content-Type", "application/problem+json")
w.Header().Set("X-Request-Id", "0192f3a4-7b1c-7d2e-8f30-4a5b6c7d8e9f")
w.WriteHeader(http.StatusForbidden)
fmt.Fprint(w, `{"type":"https://checkslip.com/problems/forbidden","title":"Forbidden","status":403,"code":"FORBIDDEN","instance":"/api/billing/balance"}`)
case "/api/slips":
w.Header().Set("Content-Type", "application/json")
if r.URL.Query().Get("cursor") == "" {
fmt.Fprint(w, `{"items":[{"request_id":"r1","outcome":"FOUND","checked_at":"2026-11-01T09:00:00Z","channel":"API"}],"next_cursor":"p2"}`)
return
}
fmt.Fprint(w, `{"items":[{"request_id":"r2","outcome":"NOT_FOUND","checked_at":"2026-11-01T08:00:00Z","channel":"API"}],"next_cursor":null}`)
default:
http.NotFound(w, r)
}
}))
}
func main() {
srv := fakeAPI()
defer srv.Close()
client := checkslip.NewClient("csk_example", checkslip.WithBaseURL(srv.URL))
res, err := client.Slips.Lookup(context.Background(), &checkslip.LookupParams{
QR: "00330006000001010306902126302105340875102TH9104F86F",
ClientReference: "order-1001",
})
if err != nil {
fmt.Println(err)
return
}
switch res.Outcome {
case checkslip.OutcomeFound:
// The bank returned the transfer. Compare it with your order
// before you accept the payment.
fmt.Println(res.Outcome, res.Slip.TransRef, res.Slip.Amount, *res.Slip.Receiver.Account)
case checkslip.OutcomePending:
fmt.Println("bank has not shown it yet; read it again after", res.RetryAfter)
default:
fmt.Println(res.Outcome)
}
}
Output: FOUND 630210534087 50.00 THB 9681
Example (Image) ¶
package main
import (
"context"
"fmt"
"os"
checkslip "github.com/Checkslip/sdk-go"
)
func main() {
client := checkslip.NewClient(os.Getenv("CHECKSLIP_API_KEY"))
f, err := os.Open("slip.jpg")
if err != nil {
fmt.Println(err)
return
}
defer f.Close()
res, err := client.Slips.Lookup(context.Background(), &checkslip.LookupParams{Image: f})
if err != nil {
fmt.Println(err)
return
}
fmt.Println(res.Outcome)
}
Output:
type SupportedBank ¶
type SupportedBank struct {
Code string `json:"code"`
BOTCode string `json:"bot_code"`
NameTH string `json:"name_th"`
NameEN string `json:"name_en"`
// SupportedSince is the day lookups for this bank started.
SupportedSince Date `json:"supported_since"`
// LandingPage reports whether checkslip.com has a page for this bank.
LandingPage bool `json:"landing_page"`
// LookbackDays is how many days back a transfer can be looked up.
// Older slips get CodeTooOld.
LookbackDays int `json:"lookback_days"`
}
SupportedBank is a bank whose transfers can be looked up.
type ThresholdBasis ¶
type ThresholdBasis string
ThresholdBasis says how Balance.Threshold was computed.
const ( ThresholdAvg7Days ThresholdBasis = "AVG_7_DAYS" ThresholdFallback10Percent ThresholdBasis = "FALLBACK_10_PERCENT" ThresholdNone ThresholdBasis = "NONE" )
Threshold bases.
type UpdateEndpointParams ¶
type UpdateEndpointParams struct {
URL *string `json:"url,omitempty"`
// Description sets the note. Use ClearDescription to remove it.
Description *string `json:"-"`
// ClearDescription removes the note.
ClearDescription bool `json:"-"`
EventTypes []EventType `json:"event_types,omitempty"`
// Enabled turns sending off (false) or back on (true). While an
// endpoint is off, new messages are not stored at all.
Enabled *bool `json:"enabled,omitempty"`
}
UpdateEndpointParams changes an endpoint. Only the fields you set are sent (JSON Merge Patch).
func (UpdateEndpointParams) MarshalJSON ¶
func (p UpdateEndpointParams) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler. It writes description as null when ClearDescription is set.
type Usage ¶
type Usage struct {
Interval UsageInterval `json:"interval"`
// AsOf is when the counts were computed. They trail real time by about
// one minute.
AsOf time.Time `json:"as_of"`
From Date `json:"from"`
To Date `json:"to"`
// Timezone is always "Asia/Bangkok".
Timezone string `json:"timezone"`
Buckets []UsageBucket `json:"buckets"`
Totals UsageCounts `json:"totals"`
}
Usage is a usage report.
type UsageBucket ¶
type UsageBucket struct {
// Start is the first day of the bucket.
Start Date `json:"start"`
UsageCounts
}
UsageBucket is the usage of one day or month.
type UsageCounts ¶
type UsageCounts struct {
Checks int64 `json:"checks"`
Charged int64 `json:"charged"`
FreeNotFound int64 `json:"free_not_found"`
FreeDuplicate int64 `json:"free_duplicate"`
FreeInvalidQR int64 `json:"free_invalid_qr"`
FreeInvalidFile int64 `json:"free_invalid_file"`
FreeQRTampered int64 `json:"free_qr_tampered"`
FreeTooOld int64 `json:"free_too_old"`
// FreeNoCredit is always 0: the API no longer counts this reason, and the
// field is going away. These lookups are counted in FreeOther. An absent
// field reads as 0, so reading it stays safe either way.
FreeNoCredit int64 `json:"free_no_credit"`
// FreeHoldExpired is going away with FreeNoCredit and is counted in
// FreeOther the same way.
FreeHoldExpired int64 `json:"free_hold_expired"`
FreeSystemError int64 `json:"free_system_error"`
// FreeOther counts lookups that were not charged with no reason given,
// including the two deprecated reasons above and any reason this version
// does not know. It reads as 0 against a server that does not send it.
FreeOther int64 `json:"free_other"`
}
UsageCounts splits lookups by whether they used a credit and, if not, why.
type UsageInterval ¶
type UsageInterval string
UsageInterval is the bucket size of a usage report.
const ( UsageDay UsageInterval = "DAY" UsageMonth UsageInterval = "MONTH" )
Usage intervals.
type UsageParams ¶
type UsageParams struct {
// Interval is DAY (the default) or MONTH.
Interval UsageInterval
// From and To are the first and last day, in Thailand time. Required.
// One request covers at most 92 days or 24 months.
From, To Date
}
UsageParams is the input of UsageService.Get.
type UsageService ¶
type UsageService struct {
// contains filtered or unexported fields
}
UsageService reads lookup counts per day or month.
func (*UsageService) Get ¶
func (s *UsageService) Get(ctx context.Context, params *UsageParams, opts ...RequestOption) (*Usage, error)
Get returns how many lookups were made, how many used a credit and how many did not, per day or month in Thailand time. Days or months without lookups are returned as zero. A lookup counts on the day its credit was settled, which can be the next day for a lookup near midnight. It needs an API key with usage:read.
type WebhookDeliveriesService ¶
type WebhookDeliveriesService struct {
// contains filtered or unexported fields
}
WebhookDeliveriesService reads and resends webhook deliveries.
func (*WebhookDeliveriesService) Get ¶
func (s *WebhookDeliveriesService) Get(ctx context.Context, deliveryID string, opts ...RequestOption) (*DeliveryDetail, error)
Get returns one delivery with its payload and attempts.
func (*WebhookDeliveriesService) List ¶
func (s *WebhookDeliveriesService) List(ctx context.Context, params *ListDeliveriesParams, opts ...RequestOption) (*Page[Delivery], error)
List returns one page of deliveries, newest first. Deliveries are kept for 30 days.
func (*WebhookDeliveriesService) ListAll ¶
func (s *WebhookDeliveriesService) ListAll(ctx context.Context, params *ListDeliveriesParams, opts ...RequestOption) *Iter[Delivery]
ListAll walks every delivery that matches params.
func (*WebhookDeliveriesService) Redeliver ¶
func (s *WebhookDeliveriesService) Redeliver(ctx context.Context, deliveryID string, opts ...RequestOption) (*Delivery, error)
Redeliver sends the same payload again, byte for byte, with the original webhook-id and a new timestamp and signature. It works on FAILED and SUCCEEDED deliveries. Your system receives the message again, so deduplicate on webhook-id.
type WebhookEndpointsService ¶
type WebhookEndpointsService struct {
// contains filtered or unexported fields
}
WebhookEndpointsService manages webhook endpoints.
func (*WebhookEndpointsService) Create ¶
func (s *WebhookEndpointsService) Create(ctx context.Context, params *CreateEndpointParams, opts ...RequestOption) (*EndpointWithSecret, error)
Create registers a URL to receive messages. The answer carries the signing secret, which is never shown again. An account can have up to 16 endpoints, and the account owner gets an email for each new one.
Create is not retried after a server error or a network failure, because a retry could register the URL twice.
func (*WebhookEndpointsService) Delete ¶
func (s *WebhookEndpointsService) Delete(ctx context.Context, endpointID string, opts ...RequestOption) error
Delete stops sending to an endpoint at once and cancels its waiting deliveries. A deleted endpoint cannot be restored. To pause sending instead, Update it with Enabled set to false.
func (*WebhookEndpointsService) Get ¶
func (s *WebhookEndpointsService) Get(ctx context.Context, endpointID string, opts ...RequestOption) (*Endpoint, error)
Get returns one endpoint. The full secret is never returned, only SecretHint.
func (*WebhookEndpointsService) List ¶
func (s *WebhookEndpointsService) List(ctx context.Context, params *ListOptions, opts ...RequestOption) (*Page[Endpoint], error)
List returns one page of the account's endpoints.
func (*WebhookEndpointsService) ListAll ¶
func (s *WebhookEndpointsService) ListAll(ctx context.Context, params *ListOptions, opts ...RequestOption) *Iter[Endpoint]
ListAll walks every endpoint.
func (*WebhookEndpointsService) Recover ¶
func (s *WebhookEndpointsService) Recover(ctx context.Context, endpointID string, params *RecoverParams, opts ...RequestOption) (*RecoverResult, error)
Recover resends every FAILED delivery of the endpoint created in the time range, each with its original webhook-id. Use it after your system was down. The range can be at most 14 days and reach back at most 30 days. The work runs in the background; follow it through the endpoint's RETRYING deliveries.
func (*WebhookEndpointsService) RotateSecret ¶
func (s *WebhookEndpointsService) RotateSecret(ctx context.Context, endpointID string, opts ...RequestOption) (*SecretRotation, error)
RotateSecret issues a new signing secret. The old one keeps signing for 24 hours, so you can switch without missing a message; VerifyWebhook accepts both secrets during that time. You cannot rotate again within those 24 hours (CodeRotationInProgress).
func (*WebhookEndpointsService) SendTest ¶
func (s *WebhookEndpointsService) SendTest(ctx context.Context, endpointID string, opts ...RequestOption) (*Delivery, error)
SendTest queues a webhook.test message to the endpoint, signed and retried like a real one. It uses no credit. Follow the returned delivery with Deliveries.Get.
func (*WebhookEndpointsService) Update ¶
func (s *WebhookEndpointsService) Update(ctx context.Context, endpointID string, params *UpdateEndpointParams, opts ...RequestOption) (*Endpoint, error)
Update changes the fields set in params. A new URL is checked again as on Create.
type WebhookEvent ¶
type WebhookEvent struct {
// ID is the webhook-id header when the event came from VerifyRequest.
ID string `json:"-"`
Type EventType `json:"type"`
// Timestamp is when the event happened.
Timestamp time.Time `json:"timestamp"`
// Data depends on Type. Decode it with SlipChecked or Test.
Data json.RawMessage `json:"data"`
}
WebhookEvent is a webhook message body. Check the signature before you trust it.
func ParseWebhookEvent ¶
func ParseWebhookEvent(body []byte) (*WebhookEvent, error)
ParseWebhookEvent decodes a message body. It does not check the signature; use WebhookVerifier.VerifyRequest for that.
func (*WebhookEvent) SlipChecked ¶
func (e *WebhookEvent) SlipChecked() (*SlipCheckedData, error)
SlipChecked decodes Data of a slip.checked event.
func (*WebhookEvent) Test ¶
func (e *WebhookEvent) Test() (*WebhookTestData, error)
Test decodes Data of a webhook.test event.
type WebhookEventTypesService ¶
type WebhookEventTypesService struct {
// contains filtered or unexported fields
}
WebhookEventTypesService lists the event types Checkslip can send.
func (*WebhookEventTypesService) List ¶
func (s *WebhookEventTypesService) List(ctx context.Context, opts ...RequestOption) ([]EventTypeInfo, error)
List returns the webhook event types.
The API wraps them in an object with nothing else in it, so the client returns the event types themselves.
type WebhookTestData ¶
type WebhookTestData struct {
EndpointID string `json:"endpoint_id"`
}
WebhookTestData is the data of a webhook.test message.
type WebhookVerifier ¶
type WebhookVerifier struct {
// contains filtered or unexported fields
}
WebhookVerifier checks the signature of webhook messages. Create it once with NewWebhookVerifier and reuse it; it is safe for concurrent use.
func NewWebhookVerifier ¶
func NewWebhookVerifier(secrets ...string) (*WebhookVerifier, error)
NewWebhookVerifier returns a verifier for the given endpoint secrets ("whsec_..."). Pass two secrets while you switch to a rotated one; a message is accepted when any of them signed it.
func (*WebhookVerifier) SetTolerance ¶
func (v *WebhookVerifier) SetTolerance(d time.Duration) *WebhookVerifier
SetTolerance changes how far webhook-timestamp may be from now. Zero or a negative value turns the timestamp check off, which you should only do in tests. It returns v so it can be chained.
func (*WebhookVerifier) Verify ¶
func (v *WebhookVerifier) Verify(header http.Header, body []byte) error
Verify checks a message from its headers and raw body. Pass the body exactly as received, before any JSON parsing.
Example ¶
package main
import (
"fmt"
"net/http"
checkslip "github.com/Checkslip/sdk-go"
)
func main() {
// Values from the Standard Webhooks test vector.
secret := "whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw"
body := []byte(`{"test": 2432232314}`)
h := http.Header{}
h.Set("webhook-id", "msg_p5jXN8AQM9LWM0D4loKWxJek")
h.Set("webhook-timestamp", "1614265330")
h.Set("webhook-signature", "v1,g0hM9SsE+OTPJTGt/tmIKtSyZlE3uFJELVlNIOLJ1OE=")
verifier, err := checkslip.NewWebhookVerifier(secret)
if err != nil {
fmt.Println(err)
return
}
// The vector was signed in 2021. With the default 5-minute tolerance
// it is refused as a replay; turn the check off only to show the
// signature itself matches.
fmt.Println(verifier.Verify(h, body))
fmt.Println(verifier.SetTolerance(0).Verify(h, body))
}
Output: checkslip: webhook timestamp outside tolerance <nil>
func (*WebhookVerifier) VerifyRequest ¶
func (v *WebhookVerifier) VerifyRequest(r *http.Request) (*WebhookEvent, error)
VerifyRequest reads the body of an incoming webhook request (up to MaxWebhookBody bytes), checks its signature and returns the parsed event. The event's ID is the webhook-id header.
Example ¶
package main
import (
"fmt"
"log"
"net/http"
"os"
checkslip "github.com/Checkslip/sdk-go"
)
func main() {
verifier, err := checkslip.NewWebhookVerifier(os.Getenv("CHECKSLIP_WEBHOOK_SECRET"))
if err != nil {
fmt.Println(err)
return
}
http.HandleFunc("/hooks/checkslip", func(w http.ResponseWriter, r *http.Request) {
ev, err := verifier.VerifyRequest(r)
if err != nil {
http.Error(w, "bad signature", http.StatusUnauthorized)
return
}
// ev.ID is the same on every resend: skip it if you have seen it.
if ev.Type == checkslip.EventSlipChecked {
data, err := ev.SlipChecked()
if err != nil {
http.Error(w, "bad payload", http.StatusBadRequest)
return
}
log.Println(data.RequestID, data.Outcome)
}
w.WriteHeader(http.StatusNoContent)
})
}
Output:
type WebhooksService ¶
type WebhooksService struct {
// Endpoints manages the URLs that receive messages.
Endpoints *WebhookEndpointsService
// Deliveries reads and resends sent messages.
Deliveries *WebhookDeliveriesService
// EventTypes lists the event types Checkslip can send.
EventTypes *WebhookEventTypesService
// contains filtered or unexported fields
}
WebhooksService manages webhook endpoints and their deliveries. To check the signature of a message your server receives, see VerifyWebhook.
func (*WebhooksService) Verify ¶
func (s *WebhooksService) Verify(header http.Header, body []byte, secrets ...string) (*WebhookEvent, error)
Verify checks the signature of a message your server received and returns the parsed event. It sends nothing, so it works on a client whose API key is never used. During a secret rotation pass both secrets.
NewWebhookVerifier and WebhookVerifier.VerifyRequest do the same without a client, and take an *http.Request directly.