What you will build
Declined attempts are counted per device. This is an illustrative application policy, not a default rule that ShieldLabs applies to every customer.Run the starting application
You need Node.js 22 or later. The starter runs without ShieldLabs keys; the final version needs a registered HTTPS hostname and matching keys. Clone the public tutorial repository, then start this standalone application:Add the integration
Stop the starter server. Follow these changes, then run the complete implementation from thefinal branch.
1
1. Prepare the keys and HTTPS hostname
The Register the app’s hostname in Integration > Domains. Put its matching Public Key and Private API Key in the private
.env copied from starter contains no key entries yet. Add the two lines below with this domain’s real values:card-testing/.env as SHIELDLABS_PUBLIC_KEY and SHIELDLABS_API_KEY. Serve this app through HTTPS on that registered hostname. The Private API Key stays on the server; a customer key does not automatically authorize localhost.2
2. Identify the action in the browser
The Only the Request ID goes to the server; the browser does not supply the risk result.
public/index.html page loads the locally served JS SDK. In public/index.js, each action takes a fresh Request ID and sends it with the action:3
3. Verify the identification on the server
server/shieldlabs.js uses @shieldlabs-ai/node and the Private API Key to find that exact Request ID in History. The action route passes it to server/orders.js, where verifyIdentification(requestId) rejects missing, stale, replayed, automated or unusable checks before this scenario’s rule is applied.4
4. Apply the card testing rule
This excerpt from
server/orders.js shows the decision’s core:server/orders.js counts declined attempts from the verified Device ID in the last 24 hours before the sample payment processor is called. The cap is three declines; the fourth attempt is refused.5
5. Run the completed application
From the repository root, compare the two versions and start the final app:A fresh clone has the remote
origin/final ref even before its local final branch exists. Open the completed app through the registered HTTPS hostname. The Node server listens on 127.0.0.1:3000 behind your reverse proxy.Follow the integration code
The completed source is in the card-testing application. Read these files in order:- Browser helper: loads the installed SDK, serializes checks and returns a request ID.
- Server verification: reads real History, checks the matching ID, rereads delayed results and rejects unusable or replayed checks.
- Scenario decision: applies this app’s rule using the verified identification and stores its sample state.
Try the completed application
- Use the approved sample card
4242424242424242for a gift-card purchase. The app records an approved synthetic payment. - Use the declined sample card
4000000000000002for three fresh attempts on the same device. The attempt list shows each decline. - Make a fourth attempt with a fresh identification. It should be refused before processing, even if you change the recipient email.
DEMO_ALLOW_RESET=1 to repeat the exercise. These controls are for a disposable demonstration, not production authorization.
Check your result
In DevTools Network, inspect the /api/purchase request and copy itsrequestId. Compare the requestId on each /api/purchase request with its History row. The three declines and the refusal must refer to the same Device ID; the refused attempt does not add another payment record. Run npm run check and npm test from card-testing to exercise failure cases locally without making more identifications.
For a real run, obtain fresh request IDs in the browser and confirm their Device IDs and signals in History. If identification returns HTTP 429, stop and check your account limits before retrying. A cookie change does not prove that the Device ID changed: compare the History rows.