Skip to content

ShopeePay Link

ShopeePay Link is an optional, free, live-mode-only payment-detection source for ShopeePay Merchant (Shopee Partner Portal) accounts. Import a signed-in session once, and the gateway’s own poller watches that account’s transaction feed server-side whenever you have unpaid transactions — covering the App Listener’s blind spots: phone off, notification missed, the listener app killed by battery savers.

Both detectors feed the exact same matching pipeline, so they can’t conflict: whichever sees the payment first marks the transaction paid and fires your webhook; the other is recorded as ignored in the inbound audit log. Poller matches show payment_source: "shopee_poll" there, plus the ShopeePay transaction id — so you can see, per transaction, which detector won.

Unofficial API notice. The poller talks to the same private API the ShopeePay Merchant dashboard uses — not an official integration. ShopeePay may change authentication, add rate limits, or invalidate sessions at any time, and accounts driven by automated clients risk suspension. Link at your own discretion. If the session breaks, nothing about payments breaks: the tenant simply degrades to App-Listener-only detection until re-linked (see Session health below).

Coverage is ShopeePay only. Payments arriving through GoPay, DANA, or other apps are still detected by their own sources (GoBiz Link for GoPay) or the App Listener — keep it running even with ShopeePay linked. These are complements, not alternatives.

Why importing a session, not a one-click login

Section titled “Why importing a session, not a one-click login”

Logging into ShopeePay Merchant requires solving a slide-to-verify CAPTCHA, which the gateway deliberately does not attempt to automate. Instead, you log in yourself in a normal browser once and hand the gateway the resulting session — this is a manual, one-time step per link (or whenever the session eventually needs replacing), not something you do on every restart.

To capture a session:

  1. Log into partner.shopee.co.id in your browser as the merchant.
  2. Open your browser’s developer tools → Network tab.
  3. Find any request made to shopeepay.shopee.co.id and copy the full value of its Cookie request header.
  4. Send that value as-is to import-session below.

A future in-app guided flow is planned so you won’t need to do this by hand — see What’s next.

All three require your live X-API-Key — a sandbox key gets VALIDATION_ERROR (sandbox has simulate-payment instead).

MethodEndpointDescription
POST/v1/shopee-link/import-sessionImport a captured browser session; stores it encrypted and activates polling
GET/v1/shopee-linkLink status
DELETE/v1/shopee-linkUnlink (idempotent) — deletes the stored session
Terminal window
curl -X POST https://api-qris-gateway.laboon.id/v1/shopee-link/import-session \
-H "X-API-Key: your_live_api_key" \
-H "Content-Type: application/json" \
-d '{ "cookie": "SPC_F=xxxxxxxx; SPC_T_ID=xxxxxxxx; __shopee_partner_website_x_token_live=xxxxxxxx.yyyyyyyy.zzzzzzzz" }'
{
"success": true,
"message": "ShopeePay account linked",
"data": { "linked": true, "status": "active", "merchant_name": "…" }
}

The cookie is validated live before anything is stored — a stale, malformed, or already-expired cookie returns 400 VALIDATION_ERROR and nothing is saved. Re-importing while already linked simply replaces the stored session (there is at most one ShopeePay link per tenant), and clears needs_relink back to active.

The session is stored encrypted (AES-256-GCM) and is never returned by any endpoint.

Terminal window
curl https://api-qris-gateway.laboon.id/v1/shopee-link -H "X-API-Key: your_live_api_key"
curl -X DELETE https://api-qris-gateway.laboon.id/v1/shopee-link -H "X-API-Key: your_live_api_key"

GET returns { "linked": false } or the link’s status, merchant_name, and timestamps. DELETE is idempotent — unlinking twice is still a 200.

  • The poller only runs for a tenant while it has at least one unpaid live transaction — an idle tenant costs ShopeePay nothing.
  • A feed settlement is matched with the same by-amount rules as the App Listener, but with two extra freshness guards on the poller’s side: a transaction is only reported if it’s newer than everything already scanned and falls inside a currently-unpaid transaction’s own time window. An older same-amount settlement can never pay a newer transaction.
  • If ShopeePay rate-limits the account, that tenant’s polling pauses briefly and resumes on its own. Other tenants are unaffected.
  • A captured session is long-lived (well over a year in practice) and is used as-is until ShopeePay actually rejects it — there’s no periodic refresh to worry about. The gateway also makes an occasional, minimal check on idle links so a revoked session is caught sooner, even before your next sale would otherwise reveal it.

GET /v1/shopee-link reports one of:

  • active — polling normally.
  • needs_relink — ShopeePay rejected the stored session (revoked, expired, or logged out elsewhere). The poller has stopped for this tenant; the App Listener keeps detecting as usual. Capture a fresh session and re-import it to restore polling — this replaces the dead session in place.

There is no error state that affects payment processing: every ShopeePay-side failure degrades to App-Listener-only detection, never to lost payments.

Importing a session today means capturing a browser cookie by hand, which is a technical step. A guided, self-service login flow (solve the CAPTCHA once in an embedded browser, no manual cookie copying) is planned for a future release so any tenant can link ShopeePay themselves.