Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Bridging Bpjs Laravel Package

virusphp/bridging-bpjs

A Laravel package for integrating BPJS Kesehatan bridging APIs, offering convenient configuration and wrappers for common endpoints so PHP applications can connect to BPJS services more easily and consistently.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish configuration:

composer require virusphp/bridging-bpjs
php artisan vendor:publish --provider="Virusphp\BridgingBpjs\BridgingBpjsServiceProvider"

The config file is the first thing to open — it holds the credentials and base URLs that every other call depends on:

BPJS_CONS_ID=xxxxx
BPJS_SECRET_KEY=xxxxx        # used for X-signature (HMAC)
BPJS_USER_KEY=xxxxx          # used as the user_key header
BPJS_VCLAIM_URL=https://new-api.bpjs-kesehatan.go.id/vclaim-rest/
BPJS_ANTROL_URL=https://new-api.bpjs-kesehatan.go.id/antreanrs/

After the config is set, the fastest end-to-end validation is a participant lookup by card number (peserta / Peserta resource). It touches every moving part — signature generation, LZ-String compression, AES decrypt — with a single request and no side effects, so if it returns a decoded array you know your credentials, clock, and decryption are all correct.

Where to look first, in order:

  1. config/bridging-bpjs.php — credential names and the two base URLs (VClaim and Antrol are separate services with separate keys).
  2. The service provider — it tells you which bindings/facades are registered, which is how you inject the client instead of using new.
  3. The LZ-String / decryptor class — this is the package's core value-add; read it before you trust it.
  4. The README method list (the facade exposes method names close to the BPJS spec: peserta, rujukan, sep, monitoring, antrean, etc.).

Implementation Patterns

Wrap it, don't call it from controllers. Inject the facade/service into your own BpjsClaimService or VclaimGateway class. BPJS method names, response envelopes, and error codes will change; your controllers shouldn't.

Handle the V2 envelope in one place. VClaim V2 returns roughly:

{
  "metaData": { "code": "200", "message": "OK" },
  "response": "<lz-string encrypted base64 blob>"
}

The package decrypts response into a JSON string; you then json_decode(). Build a single unwrap() helper that returns ['code' => ..., 'data' => ...] or throws a domain exception, and use it everywhere. Never let a raw envelope reach your views.

Branch on metaData.code, not on HTTP status. BPJS returns HTTP 200 with a populated error code for business failures (201 = data not found, 202 = invalid credential, and so on). Treat 000/non-200 codes as application-level outcomes, not exceptions.

Cache reference data aggressively. Poli lists, dokter lists, and faskes lookups change rarely. A 24-hour cache keyed by faskes + endpoint removes most of your latency and protects you when BPJS is slow or down. Do not cache peserta or SEP checks.

SEP creation is a workflow, not a call. The day-to-day shape is:

  1. rujukan lookup → validate the referral exists and is still valid
  2. sep create → capture the returned SEP number
  3. Persist locally (patient, SEP number, timestamp, raw request id) in the same DB transaction that marks your own record as "bridged"
  4. Print / return the SEP to the operator

Steps 2 and 3 must be reconcilable. Store the raw decrypted response alongside the SEP number so a failed DB write can be replayed without re-issuing a SEP.

Antrol is a separate track. Booking, calling, and canceling queue numbers (antrean) uses different credentials and often a different base URL. Treat Antrol as its own service class even if the package exposes both through one facade — you'll want to fake one and test the other.

Log a correlation id per request. Compose something like noKartu-timestamp-endpoint and log it with the raw encrypted response before decryption. BPJS dispute resolution and your own debugging both become possible; without it, a decryption failure is unreproducible.

Test with Http::fake(). Because the package ultimately uses the Laravel HTTP client against the configured base URLs, you can stub the endpoints in feature tests and keep the real decryptor in the loop (or stub that too, with a fixture of a known-good encrypted payload).

Gotchas and Tips

Clock drift silently breaks everything. X-timestamp is UNIX seconds and feeds the X-signature computation. A few minutes of drift returns an auth error with no hint about time. Run NTP on every app server; never cache a timestamp across requests.

Secret key and user key are different values. X-signature = base64(sha256(cons_id + "&" + timestamp, secret_key)), while user_key is sent as its own header. Swapping them produces a generic 401 that looks like a network problem. This is the single most common setup mistake.

The response field is triple-encoded. Decrypt → LZ-String decompress → JSON string → json_decode. If a method hands you back a string that looks like JSON, you're one json_decode short. If it hands you back gibberish, you're one decompress short. Keep the two layers separable so you can test them independently.

Error responses are often not encrypted. When BPJS rejects the request outright, response is null and the useful message is in metaData.message. Guard every decrypt call with a null/empty check or you'll throw a confusing LZ-String exception on top of the real error.

Not every endpoint is encrypted on the way out. Some VClaim V2 methods take a plain query string, others take an encrypted body. Check each method's signature in the source rather than assuming symmetry — a wrong assumption usually surfaces as a 202 invalid-credential error, which sends you chasing the wrong bug.

Never retry SEP creation blindly. Retry-with-backoff is correct for rujukan, monitoring, and reference lookups. For SEP insertion and Antrol booking, a timeout may still have created the record server-side. Check locally first, and consider the SEP number itself as your idempotency key.

UTF-8 handling in LZ-String. The PHP port operates on character positions; if you round-trip payloads through a non-UTF-8-aware column or a json_encode with escaped unicode, decompression can silently produce wrong bytes. Store and log as UTF-8 end to end.

Antrol usually needs IP whitelisting. Requests from a dev laptop will fail even with correct keys. Get the production egress IP registered early, and plan for it in staging.

Pin the exact version. A 41-star package with a 2025 release date is actively churning against a moving government API. Use "virusphp/bridging-bpjs": "x.y.z" (not ^x.y), and keep a fork or a local patch path ready — you will eventually need to fix a decryptor edge case or add an endpoint before the maintainer does.

Useful extension points: the service provider's bindings (swap the client for a fake), the config file (per-environment base URLs), and the decryptor class (unit-test it against fixtures from the official BPJS encryption tool). If the package doesn't expose a decrypt method publicly, wrap or extend the class rather than duplicating the algorithm — LZ-String + AES with a subtly wrong key derivation is very hard to debug from the outside.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings