Documentation
¶
Overview ¶
Package snap implements Bank Indonesia's SNAP (Standar Nasional Open API Pembayaran) standard, document version 1.0.2 (September 2024).
This package holds only the core primitives shared across every SNAP service: signing (SignSymmetric, SignAsymmetric, BuildStringToSignTransaction), token lifecycle (Token, TokenManager), server-side inbound-request verification (ServerVerifier, KeyStore), header assembly with per-PJP quirk hooks (HeaderBuilder), response-code parsing and the authoritative status check (ParseResponseCode, CheckResponseStatus, the Err* sentinels), the request/response transport (Transport, Envelope), and the shared Money amount type — see the project documentation at https://snap-bi.jamalkya.lol/docs/introduction/ for a walkthrough of these core concepts.
Package layout ¶
Per-service request/response types and their calling functions are split into subpackages that mirror the ASPI SNAP Developer Site's own top-level "API Services" categories 1:1 (apidevportal.aspi-indonesia.or.id/api-services) — so when the portal adds or changes an endpoint, which package to touch is never a judgment call:
- github.com/koriebruh/go-snap-bi/registration — Registrasi (Service Codes 01-10, 81): CardRegistration and its Set Limit/Inquiry/ Unbinding variants, VerifyOTP, OTP, AccountCreation, AccountBinding and its Inquiry/Unbinding variants, GetOAuthURL.
- github.com/koriebruh/go-snap-bi/balanceinfo — Informasi Saldo (Service Code 11): BalanceInquiry. The portal's smallest category — one endpoint, one file, one package, deliberately not folded into a bigger one so it stays easy to find.
- github.com/koriebruh/go-snap-bi/transactionhistory — Riwayat Transaksi (Service Codes 12-14): TransactionHistoryList, TransactionHistoryDetail, BankStatement.
- github.com/koriebruh/go-snap-bi/transfercredit — Transfer Kredit (Service Codes 15-53, 75-78, 43 endpoints): Account Inquiry, Trigger Transfer, Virtual Account, Transaction Status Inquiry (Bank), Customer Top Up, Bulk Cash In, Transfer to Bank, Transfer to OTC, MPM/QR, Transaction Status Inquiry (non-bank).
- github.com/koriebruh/go-snap-bi/transferdebit — Transfer Debit (Service Codes 54-72, 79-80, 21 endpoints): Direct Debit, CPM, Direct Debit BI-FAST, Auth Payment.
The portal's remaining two categories don't get their own subpackage:
- Keamanan (Service Codes 73-74, plus two sandbox-only "Signature Auth"/"Signature Service" testing utilities that are not real transactional endpoints and are out of scope): the Access Token B2B/B2B2C endpoints are infrastructure every other package depends on to get a token in the first place, not a peer domain endpoint — they live in this package's own token.go (TokenManager.AccessTokenB2B, TokenManager.AccessTokenB2B2C), never in a subpackage.
- Administrasi has no API endpoints at all on the portal — it's onboarding/account-management documentation only (IP allowlisting, key management, registration paperwork). Nothing to implement, and nothing will ever need to move here.
Every subpackage imports this package (aliased "snap" regardless of its own package name, since the import path's basename doesn't match) for the shared types above; internal/snaptest holds a single test-only HeaderBuilder helper shared across all of them, not part of the public API.
Adding a new endpoint ¶
Find its Service Code, look up which of the 7 portal categories above it falls under, and add the file to that category's package (or this package's core, only for something Keamanan-shaped: shared authentication/signing infrastructure, not a per-service binding). Never split one portal category across two packages and never fold two categories into one, even if it looks like a small saving right now — that 1:1 mapping is the entire point of this layout.
Status ¶
All 7 of the portal's API Service categories are accounted for: Registrasi, Informasi Saldo, Riwayat Transaksi, Transfer Kredit, and Transfer Debit are fully implemented (79 endpoints total across the five); Keamanan's transactional endpoints live in this package's own token.go, as described above; Administrasi has no endpoints to implement. See CONFORMANCE.md and SPEC_COVERAGE.md for the endpoint-by-endpoint traceability against the specification, and the project documentation at https://snap-bi.jamalkya.lol/docs/introduction/ for a full reference per package.
Index ¶
- Constants
- Variables
- func BuildStringToSignAccessToken(clientID, timestamp string) string
- func BuildStringToSignTransaction(method, endpointURL, accessToken string, body []byte, timestamp string, ...) string
- func CheckResponseStatus(responseCode string, httpStatus int) error
- func ParseRSAPrivateKeyPEM(pemBytes []byte) (crypto.Signer, error)
- func ParseResponseCode(code string) (httpStatus int, serviceCode, caseCode string, err error)
- func ResponseCodeError(code string) error
- func SignAsymmetric(signer crypto.Signer, stringToSign string) (string, error)
- func SignSymmetric(clientSecret, stringToSign string) string
- func TruncateForError(s string) string
- func VerifyAsymmetric(pub crypto.PublicKey, stringToSign, signature string) error
- func VerifySymmetric(clientSecret, stringToSign, signature string) bool
- type DefaultProfile
- type Envelope
- type GrantType
- type HeaderBuilder
- type IncomingRequest
- type KeyStore
- type Money
- type Profile
- type ServerVerifier
- type SignatureMode
- type Token
- type TokenManager
- type Transport
Constants ¶
const DefaultTimestampLayout = "2006-01-02T15:04:05.000-07:00"
DefaultTimestampLayout is the Go time layout equivalent to the SNAP standard's yyyy-MM-ddTHH:mm:ss.SSSTZD timestamp format.
const DefaultTimestampWindow = 5 * time.Minute
DefaultTimestampWindow is the freshness tolerance used when ServerVerifier.TimestampWindow is left at its zero value. The standard requires timestamp freshness checking, so an unconfigured ServerVerifier fails closed with this sane default rather than silently skipping the check — matching every other optional field in this package (Now, Profile, HTTPClient), where absent means "safe default," never "disabled."
const DisableTimestampFreshnessCheck time.Duration = -1
DisableTimestampFreshnessCheck is a sentinel TimestampWindow value that explicitly disables freshness checking. Distinct from the zero value on purpose, so "I forgot to set this" (zero value, gets DefaultTimestampWindow) and "I deliberately don't want this" (this sentinel) can't be confused.
Variables ¶
var ( ErrBadRequest = errors.New("snap: bad request") ErrForbidden = errors.New("snap: forbidden") ErrNotFound = errors.New("snap: not found") ErrInternalServerError = errors.New("snap: internal server error") ErrTimeout = errors.New("snap: timeout") // ErrUnmappedResponseCode is returned by ResponseCodeError (and the // status-derived fallbacks in transport.go/token.go) for an HTTP-status // class with no dedicated sentinel above — including statuses SNAP's own // table doesn't document (e.g. 429, 502, 520-527) that a proxy, WAF, or // gateway in front of the real server commonly returns — and for a // responseCode that doesn't parse at all (wrong length or non-digit // characters), which is equally "no class we can name." Exported so // callers can errors.Is against this bucket instead of losing all // machine-readable signal for exactly the class of error most likely to // come from infrastructure rather than the SNAP server itself. ErrUnmappedResponseCode = errors.New("snap: unmapped response code class") )
Sentinel errors for SNAP response-code HTTP-status classes, one per class documented in the standard's response-code table. Match via errors.Is regardless of the specific case code.
var ( ErrNoPEMBlock = errors.New("snap: no PEM block found") ErrNotRSAKey = errors.New("snap: not an RSA key") )
Sentinel errors returned by ParseRSAPrivateKeyPEM, wrapped with %w so callers can distinguish failure modes via errors.Is.
var ( ErrNotRSASigner = errors.New("snap: not an RSA signer") ErrWeakRSAKey = errors.New("snap: RSA key smaller than minimum size") // ErrSignatureEncoding: the signature is neither Base64 nor hex of the expected size. ErrSignatureEncoding = errors.New("snap: signature is not Base64 (or hex) of the expected size") )
Sentinel errors returned by SignAsymmetric and VerifyAsymmetric, wrapped with %w so callers can distinguish failure modes via errors.Is.
var ErrEmptyClientSecret = errors.New("snap: verify: KeyStore.ClientSecret returned an empty string")
ErrEmptyClientSecret is returned when a KeyStore's ClientSecret lookup succeeds but returns an empty string. Deliberately distinct from ErrSignatureMismatch: VerifySymmetric already refuses to treat an empty secret as "verifies against the empty string" (an empty HMAC key is a publicly computable MAC), but that refusal is otherwise indistinguishable from a genuine tampered-signature failure to a caller checking errors.Is(err, ErrSignatureMismatch) — this sentinel lets a caller tell "your KeyStore has a bug" apart from "this request was tampered with or forged."
var ErrNoKeyStore = errors.New("snap: verify: KeyStore is not set")
ErrNoKeyStore is returned when a ServerVerifier is used with a nil KeyStore, instead of panicking on the first lookup.
var ErrSignatureMismatch = errors.New("snap: signature mismatch")
ErrSignatureMismatch is returned when a signature fails to verify against an otherwise-successfully-looked-up key/secret, for both signing modes — giving callers one mode-independent errors.Is check rather than needing to know that VerifyAsymmetric happens to return its own error on mismatch.
This is deliberately NOT returned for a KeyStore/config mistake — an empty ClientSecret (see ErrEmptyClientSecret) or an unusable RSA key (ErrNotRSASigner, ErrWeakRSAKey, both from signing.go) are distinct, separately errors.Is-matchable failures. This lets a caller who wants to distinguish the two cases (e.g. alerting differently on a tampered/forged request than on its own configuration bug) do so via errors.Is against the specific sentinel — this package does not itself decide what a caller's monitoring or alerting should key on.
Functions ¶
func BuildStringToSignAccessToken ¶
BuildStringToSignAccessToken builds the stringToSign for a SNAP access token request: clientID + "|" + timestamp.
func BuildStringToSignTransaction ¶
func BuildStringToSignTransaction(method, endpointURL, accessToken string, body []byte, timestamp string, symmetric bool) string
BuildStringToSignTransaction builds the stringToSign for a SNAP transaction request from the bytes sent on the wire. The body is minified first, as the standard requires (Lowercase(HexEncode(SHA-256(minify(RequestBody))))), so whitespace outside JSON strings does not change the signature; a body that is not JSON is hashed as is, and an empty body hashes the empty string.
symmetric formula: HTTPMethod:EndpointUrl:AccessToken:HexSHA256(minify(body)):TimeStamp asymmetric formula: HTTPMethod:EndpointUrl:HexSHA256(minify(body)):TimeStamp
func CheckResponseStatus ¶
CheckResponseStatus is the authoritative success/failure check for a decoded SNAP response: the transport-level HTTP status always wins over what responseCode claims. A non-2xx HTTP status is never treated as success, even if responseCode's own embedded class says otherwise (e.g. a stale cached body from a misbehaving intermediary, or an out-of-band 500 with a templated "success" body) — envelopeError alone cannot catch this, since it only looks at the code's own embedded status, never the transport's actual one.
Exported so the registration, balanceinfo, transactionhistory, transfercredit, and transferdebit subpackages' calling functions can invoke it; previously unexported when every endpoint lived in this same package.
func ParseRSAPrivateKeyPEM ¶
ParseRSAPrivateKeyPEM parses a PKCS#1 or PKCS#8 PEM-encoded RSA private key. It is a convenience helper; callers that already hold a crypto.Signer (e.g. from an HSM/KMS) do not need it.
func ParseResponseCode ¶
ParseResponseCode splits a 7-character SNAP responseCode into HTTPStatus(3) + ServiceCode(2) + CaseCode(2). It returns a non-nil error for any code not exactly 7 digits or containing non-digit characters.
func ResponseCodeError ¶
ResponseCodeError parses code and returns the sentinel error matching its HTTP-status class, wrapped with the raw code via %w so callers can recover it while still using errors.Is against the class sentinel. For a class with no matching sentinel, it returns ErrUnmappedResponseCode wrapped the same way, never nil.
func SignAsymmetric ¶
SignAsymmetric signs stringToSign with SHA256withRSA (PKCS#1 v1.5) using signer, returning standard Base64. signer is a crypto.Signer rather than a concrete *rsa.PrivateKey so that HSM/KMS-backed keys can be plugged in without an API change.
func SignSymmetric ¶
SignSymmetric computes the HMAC-SHA512 signature of stringToSign using clientSecret as the key, returning standard Base64 (the encoding SNAP participants use for X-SIGNATURE).
func TruncateForError ¶
TruncateForError caps an untrusted string before it's interpolated into an error message, so a pathologically large input can't produce a proportionally large (and typically logged) error string. Exported so the domain subpackages' own untrusted-input error paths (e.g. registration.CardRegistrationInquiry's custIdMerchant) can use it too.
func VerifyAsymmetric ¶
VerifyAsymmetric verifies a SHA256withRSA signature of stringToSign (standard Base64, or hex from v0.1.x) against pub, which must be an *rsa.PublicKey. It returns a non-nil error if pub is not an RSA key, the signature is not decodable, or it does not verify.
func VerifySymmetric ¶
VerifySymmetric reports whether signature is the valid HMAC-SHA512 signature of stringToSign under clientSecret, using a constant-time comparison.
Types ¶
type DefaultProfile ¶
DefaultProfile implements the standard SNAP behavior exactly. Domain and Version are integration-specific, so callers must construct one, e.g. DefaultProfile{Domain: "openapi.example.com", Version: "v1.0"}.
func (DefaultProfile) BuildPath ¶
func (p DefaultProfile) BuildPath(serviceGroup, productType string) string
BuildPath returns "/{domain}/{version}/{service-group}/{product-type}". Version defaults to "v1.0" when unset.
func (DefaultProfile) TimestampLayout ¶
func (p DefaultProfile) TimestampLayout() string
TimestampLayout returns DefaultTimestampLayout.
type Envelope ¶
type Envelope struct {
StatusCode int
ResponseCode string
ResponseMessage string
Raw json.RawMessage
}
Envelope is the generic decoded response body for a SNAP request, plus the transport-level HTTP status. Per-service bindings (in this same package) unmarshal Raw into their own typed response.
StatusCode matters because ResponseCode is only reliable when the server actually returned SNAP's own error shape — a body from a proxy, WAF, or gateway in front of it may carry no ResponseCode at all. A per-service binding with no ResponseCode to fall back on should use sentinelForHTTPStatus(env.StatusCode) in that case, the same pattern TokenManager already uses internally.
type GrantType ¶
type GrantType string
GrantType identifies the SNAP OAuth-style grant used for an access-token request.
const ( GrantTypeClientCredentials GrantType = "client_credentials" GrantTypeAuthorizationCode GrantType = "AUTHORIZATION_CODE" GrantTypeRefreshToken GrantType = "REFRESH_TOKEN" )
Grant types defined by the standard. Values match the wire format exactly (note the inconsistent casing is the standard's, not a typo).
type HeaderBuilder ¶
type HeaderBuilder struct {
Method string // HTTP method, e.g. "POST"
EndpointURL string // full endpoint URL used in the signed string
Body []byte // exact request body bytes
B2B2C bool // whether this is a B2B2C request
AccessToken string // Authorization bearer token
AuthorizationCustomer string // Authorization-Customer bearer token; mandatory when B2B2C is true
DeviceID string // X-DEVICE-ID; mandatory when B2B2C is true
ClientKey string
PartnerID string
ExternalID string
ChannelID string
Origin string // optional; header omitted when empty
// B2B2C-only, genuinely optional; each header is omitted when empty.
IPAddress string
Latitude string
Longitude string
Profile Profile
Symmetric bool
ClientSecret string // used when Symmetric is true
Signer crypto.Signer // used when Symmetric is false
}
HeaderBuilder assembles the mandatory SNAP header set for a transaction request. Symmetric selects which of Slice 1's signing functions is used, matching BuildStringToSignTransaction's own parameter, so the caller supplies exactly one of ClientSecret or Signer.
type IncomingRequest ¶
type IncomingRequest struct {
Method string // HTTP method, e.g. "POST"
EndpointURL string // full endpoint URL exactly as used in the signing formula
Body []byte // exact request body bytes as received
Timestamp string // X-TIMESTAMP header value
ClientKey string // X-CLIENT-KEY header value
Signature string // X-SIGNATURE header value
AccessToken string // Authorization header's token, without the "Bearer " prefix; required for symmetric transaction verification, ignored otherwise
ExternalID string // X-EXTERNAL-ID header value; carried through for a caller's own replay defense (see VerifyTransactionRequest's doc comment) but not checked by this package
}
IncomingRequest holds the fields a ServerVerifier needs, extracted by the caller from whatever HTTP framework they use — this package doesn't parse *http.Request itself, to stay framework-agnostic.
type KeyStore ¶
type KeyStore interface {
PublicKey(clientKey string) (crypto.PublicKey, error)
ClientSecret(clientKey string) (string, error)
}
KeyStore is caller-implemented — key/secret storage is an application concern (DB, vault, etc.), not something this package should own.
For an unrecognized clientKey, an implementation must return a non-nil error (its own sentinel, or any distinct error — this package doesn't require a specific one) rather than a zero-value crypto.PublicKey/"" with a nil error: this package's callers treat a nil error as "the lookup succeeded," so a zero-value-with-nil-error response for an unknown key is indistinguishable from a misconfigured store and can surface as a confusing ErrSignatureMismatch/ErrEmptyClientSecret instead of a clear "unknown client" failure.
type Money ¶
Money is the shared {value, currency} amount shape used across every per-service request or response that carries a monetary value. value is a decimal string (e.g. "200000.00"); currency is ISO 4217 (e.g. "IDR").
Extracted into its own file (previously defined in balance_inquiry.go, now in the balanceinfo subpackage) because it is a core cross-cutting type shared by every subpackage, not specific to BalanceInquiry.
type Profile ¶
type Profile interface {
// TimestampLayout returns the Go time layout used to format X-TIMESTAMP
// and the timestamp segment of the signed string.
TimestampLayout() string
// BuildPath returns the request URI path for a given service group and
// product type, per spec §2.1.11.
BuildPath(serviceGroup, productType string) string
}
Profile provides the integration-specific hooks the SNAP standard leaves open to each PJP: timestamp formatting and URI path construction. A bank-specific deviation is expressed as a small struct embedding DefaultProfile and overriding one method, not a fork of HeaderBuilder.
type ServerVerifier ¶
type ServerVerifier struct {
KeyStore KeyStore
Mode SignatureMode // transaction-level mode; access-token requests are always asymmetric regardless of this
TimestampWindow time.Duration // freshness tolerance; zero means DefaultTimestampWindow, DisableTimestampFreshnessCheck explicitly disables it
Profile Profile // optional; nil means DefaultProfile{}, used only for TimestampLayout when parsing Timestamp
Now func() time.Time // optional; nil means time.Now, injectable for tests
}
ServerVerifier verifies incoming SNAP requests on the Penyedia Layanan (server) side — the inverse of the client-side signing in HeaderBuilder and TokenManager.
func (*ServerVerifier) VerifyAccessTokenRequest ¶
func (v *ServerVerifier) VerifyAccessTokenRequest(req IncomingRequest) error
VerifyAccessTokenRequest verifies an incoming access-token request's signature. Access-token requests are always asymmetric, regardless of v.Mode, matching TokenManager which always signs them asymmetrically.
func (*ServerVerifier) VerifyTransactionRequest ¶
func (v *ServerVerifier) VerifyTransactionRequest(req IncomingRequest) error
VerifyTransactionRequest verifies an incoming transaction request's signature per the pre-agreed SignatureMode. A KeyStore lookup succeeding does not by itself make the request valid — the signature check always runs and is what determines pass/fail.
A nil return proves the caller possesses the shared secret or private key for req.ClientKey and that the signature is bound to req.AccessToken as given — it does not itself validate that req.AccessToken is genuine, unexpired, or was actually issued to this client. Token issuance and introspection are out of this package's scope; callers who need that check must perform it separately.
This function also does not itself defend against replay: within TimestampWindow, a captured, otherwise-untampered request verifies identically on a second delivery. The standard requires X-EXTERNAL-ID uniqueness per client per day for exactly this reason — req.ExternalID is carried through so a caller can maintain their own dedup store (a cache, a database unique constraint) keyed on ClientKey+ExternalID, but this package does not do so itself, the same way it does not own KeyStore's storage.
type SignatureMode ¶
type SignatureMode int
SignatureMode is the transaction-level signing mode agreed with a partner at registration time, per the standard's own wording — this is config, not a per-request runtime choice. The zero value is SignatureModeAsymmetric to match HeaderBuilder.Symmetric's zero value (false, i.e. asymmetric) — the two are meant to mirror each other for the same partner configuration, on the client and server side respectively.
const ( SignatureModeAsymmetric SignatureMode = iota // default zero value; matches HeaderBuilder{}'s default (Symmetric: false) SignatureModeSymmetric // matches HeaderBuilder{Symmetric: true} )
The two SignatureMode values a ServerVerifier can be configured with.
type Token ¶
type Token struct {
AccessToken string
TokenType string
ExpiresIn time.Duration
RefreshToken string // set for B2B2C only
}
Token is the result of a B2B2C access-token exchange.
type TokenManager ¶
type TokenManager struct {
BaseURL string
ClientKey string
Signer crypto.Signer
// HTTPClient is optional; nil means a client with defaultHTTPTimeout
// and a CheckRedirect policy that never follows a redirect (see
// Transport.HTTPClient's doc comment for why: a redirect on an
// access-token request would carry X-SIGNATURE and X-CLIENT-KEY to
// whatever host it names). A caller-supplied HTTPClient must set an
// equivalent policy itself.
HTTPClient *http.Client
Profile Profile
Now func() time.Time // optional; nil means time.Now
// contains filtered or unexported fields
}
TokenManager fetches and (for B2B) caches SNAP access tokens. Access-token requests are always asymmetric-signed, both B2B and B2B2C, regardless of the signing mode agreed for transaction requests.
func (*TokenManager) AccessTokenB2B ¶
func (m *TokenManager) AccessTokenB2B(ctx context.Context) (string, error)
AccessTokenB2B returns a cached client_credentials access token, fetching (and caching) a new one if none is cached or the cached one is at or past its computed expiry. Guarded by a mutex so concurrent callers share one in-flight fetch's result rather than each firing a request.
func (*TokenManager) AccessTokenB2B2C ¶
func (m *TokenManager) AccessTokenB2B2C(ctx context.Context, grantType GrantType, code string) (Token, error)
AccessTokenB2B2C exchanges an authorization code or refresh token for a per-end-user Token. Not cached — the caller owns customer-session scoping.
type Transport ¶
type Transport struct {
// HTTPClient is optional; nil means a client with defaultHTTPTimeout and
// a CheckRedirect policy that never follows a redirect (see Do). A
// caller-supplied HTTPClient bypasses that policy entirely — no SNAP
// endpoint has a legitimate reason to redirect, and a 307/308 preserves
// method and body, so a caller-supplied client MUST set an equivalent
// CheckRedirect (returning http.ErrUseLastResponse, or otherwise
// refusing every redirect) or risk leaking the signed X-SIGNATURE,
// X-CLIENT-KEY, X-PARTNER-ID headers and the full request body —
// account numbers, amounts, customer PII — to whatever host a
// compromised or misconfigured server redirects to.
HTTPClient *http.Client
}
Transport sends a signed request built by a HeaderBuilder and decodes the response into an Envelope. It does not retry, rate-limit, or interpret a non-2xx responseCode as a Go error — callers do that via ResponseCodeError.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package balanceinfo implements Bank Indonesia's SNAP "Informasi Saldo" API Service category (Service Code 11): BalanceInquiry, the portal's smallest category with a single endpoint.
|
Package balanceinfo implements Bank Indonesia's SNAP "Informasi Saldo" API Service category (Service Code 11): BalanceInquiry, the portal's smallest category with a single endpoint. |
|
internal
|
|
|
snaptest
Package snaptest provides shared test-only helpers for go-snap-bi's registration, balanceinfo, transactionhistory, transfercredit, and transferdebit subpackages.
|
Package snaptest provides shared test-only helpers for go-snap-bi's registration, balanceinfo, transactionhistory, transfercredit, and transferdebit subpackages. |
|
Package registration implements Bank Indonesia's SNAP "Registrasi" API Service category (Service Codes 01-10, 81): card and account binding/unbinding, OTP verification, account creation, and the OAuth URL used to start an account-binding flow.
|
Package registration implements Bank Indonesia's SNAP "Registrasi" API Service category (Service Codes 01-10, 81): card and account binding/unbinding, OTP verification, account creation, and the OAuth URL used to start an account-binding flow. |
|
Package transactionhistory implements Bank Indonesia's SNAP "Riwayat Transaksi" API Service category (Service Codes 12-14): TransactionHistoryList, TransactionHistoryDetail, and BankStatement.
|
Package transactionhistory implements Bank Indonesia's SNAP "Riwayat Transaksi" API Service category (Service Codes 12-14): TransactionHistoryList, TransactionHistoryDetail, and BankStatement. |
|
Package transfercredit implements Bank Indonesia's SNAP "Transfer Kredit" API Service category (Service Codes 15-53, 75-78, 43 endpoints): Account Inquiry, Trigger Transfer, Virtual Account, Transaction Status Inquiry (Bank), Customer Top Up, Bulk Cash In, Transfer to Bank, Transfer to OTC, and MPM/QR.
|
Package transfercredit implements Bank Indonesia's SNAP "Transfer Kredit" API Service category (Service Codes 15-53, 75-78, 43 endpoints): Account Inquiry, Trigger Transfer, Virtual Account, Transaction Status Inquiry (Bank), Customer Top Up, Bulk Cash In, Transfer to Bank, Transfer to OTC, and MPM/QR. |
|
Package transferdebit implements Bank Indonesia's SNAP "Transfer Debit" API Service category (Service Codes 54-72, 79-80, 21 endpoints): Direct Debit, CPM, Direct Debit BI-FAST, and Auth Payment.
|
Package transferdebit implements Bank Indonesia's SNAP "Transfer Debit" API Service category (Service Codes 54-72, 79-80, 21 endpoints): Direct Debit, CPM, Direct Debit BI-FAST, and Auth Payment. |